Optimize RmlUi render passes

This commit is contained in:
Luke Street
2026-06-14 20:15:45 -06:00
parent 4c374906d2
commit 10308b0ccb
9 changed files with 311 additions and 127 deletions
+11 -3
View File
@@ -239,13 +239,16 @@ void end_frame() noexcept {
presentSource.size.width, presentSource.size.height);
wgpu::BindGroup rmlBindGroup;
bool rmlOverlay = false;
#if AURORA_ENABLE_RMLUI
if (rmlui::is_initialized()) {
rmlBindGroup = rmlui::record_frame(viewport);
auto rmlFrame = rmlui::record_frame(viewport);
rmlBindGroup = std::move(rmlFrame.bindGroup);
rmlOverlay = rmlFrame.overlay;
}
#endif
gfx::end_frame([rmlBindGroup = std::move(rmlBindGroup), viewport,
gfx::end_frame([rmlBindGroup = std::move(rmlBindGroup), rmlOverlay, viewport,
imguiDrawData = std::move(imguiDrawData)](wgpu::CommandEncoder& encoder) {
wgpu::Texture currentTexture;
wgpu::TextureView currentView;
@@ -267,7 +270,7 @@ void end_frame() noexcept {
const bool canPresent = currentTexture && currentView;
if (canPresent) {
wgpu::BindGroup presentBindGroup;
if (rmlBindGroup) {
if (rmlBindGroup && !rmlOverlay) {
presentBindGroup = rmlBindGroup;
} else {
const auto& resampledSource = webgpu::resample_present_source(encoder, viewport);
@@ -294,6 +297,11 @@ void end_frame() noexcept {
pass.SetViewport(viewport.left, viewport.top, viewport.width, viewport.height, viewport.znear, viewport.zfar);
pass.Draw(3);
if (rmlBindGroup && rmlOverlay) {
pass.SetPipeline(webgpu::g_CopyPremultipliedAlphaPipeline);
pass.SetBindGroup(0, rmlBindGroup, 0, nullptr);
pass.Draw(3);
}
pass.End();
}
{
+35 -3
View File
@@ -4,6 +4,7 @@
#include <thread>
#include <RmlUi/Core.h>
#include <RmlUi/Core/Element.h>
#include <RmlUi_Backend.h>
#include <RmlUi_Platform_SDL.h>
#include <tracy/Tracy.hpp>
@@ -82,6 +83,32 @@ void ensure_render_target(Rml::Vector2i dimensions) noexcept {
s_renderTargetCopyBindGroup = webgpu::create_copy_bind_group(s_renderTarget);
}
bool element_has_visible_backdrop_filter(const Rml::Element* element) noexcept {
if (element == nullptr || !element->IsVisible(true)) {
return false;
}
const auto& computed = element->GetComputedValues();
if (computed.opacity() > 0.f && computed.has_backdrop_filter()) {
return true;
}
const int childCount = element->GetNumChildren();
for (int childIndex = 0; childIndex < childCount; ++childIndex) {
if (element_has_visible_backdrop_filter(element->GetChild(childIndex))) {
return true;
}
}
return false;
}
bool context_has_visible_backdrop_filter(Rml::Context* context) noexcept {
if (context == nullptr) {
return false;
}
return element_has_visible_backdrop_filter(context->GetRootElement());
}
struct MappedPoint {
Rml::Vector2f position;
bool valid = false;
@@ -408,7 +435,7 @@ void handle_event(SDL_Event& event) noexcept {
RmlSDL::InputEventHandler(g_context, window::get_sdl_window(), event);
}
wgpu::BindGroup record_frame(const webgpu::Viewport& presentViewport) noexcept {
RecordedFrame record_frame(const webgpu::Viewport& presentViewport) noexcept {
if (g_context == nullptr) {
return {};
}
@@ -422,10 +449,12 @@ wgpu::BindGroup record_frame(const webgpu::Viewport& presentViewport) noexcept {
sync_context_metrics(dim);
g_context->Update();
const bool needsBackdrop = context_has_visible_backdrop_filter(g_context);
auto* renderInterface = get_render_interface();
renderInterface->SetWindowSize(g_context->GetDimensions());
renderInterface->BeginFrame(s_renderTarget, webgpu::present_source());
renderInterface->BeginFrame(s_renderTarget, webgpu::present_source(),
needsBackdrop ? BaseLayerContent::Scene : BaseLayerContent::Transparent);
Backend::BeginFrame();
g_context->Render();
@@ -435,7 +464,10 @@ wgpu::BindGroup record_frame(const webgpu::Viewport& presentViewport) noexcept {
// We didn't render anything
return {};
}
return s_renderTargetCopyBindGroup;
return {
.bindGroup = s_renderTargetCopyBindGroup,
.overlay = !needsBackdrop,
};
}
void shutdown() noexcept {
+6 -1
View File
@@ -9,9 +9,14 @@
namespace aurora::rmlui {
struct RecordedFrame {
wgpu::BindGroup bindGroup;
bool overlay = false;
};
void initialize(const AuroraWindowSize& size) noexcept;
void handle_event(SDL_Event& event) noexcept;
wgpu::BindGroup record_frame(const webgpu::Viewport& presentViewport) noexcept;
RecordedFrame record_frame(const webgpu::Viewport& presentViewport) noexcept;
void shutdown() noexcept;
} // namespace aurora::rmlui
+174 -39
View File
@@ -27,6 +27,7 @@ namespace {
Module Log("aurora::rmlui::RenderInterface");
constexpr size_t rmlBufferOffsetAlignment = 4;
constexpr float FilterEpsilon = 0.0001f;
struct Image {
std::unique_ptr<uint8_t[]> data;
@@ -53,6 +54,23 @@ struct CompiledShaderData {
GradientUniformBlock gradient;
};
enum class FilterType {
Opacity,
Blur,
DropShadow,
ColorMatrix,
MaskImage,
};
struct CompiledFilter {
FilterType type = FilterType::Blur;
float opacity = 1.f;
float sigma = 0.f;
Rml::Vector2f offset;
Rml::ColourbPremultiplied color;
Rml::Matrix4f colorMatrix;
};
Image get_image(const Rml::String& source) {
FileInterface_SDL fileInterface;
const Rml::FileHandle file = fileInterface.Open(source);
@@ -158,6 +176,74 @@ Rml::ColumnMajorMatrix4f to_shader_matrix(const Rml::Matrix4f& matrix) {
}
}
bool is_identity_matrix(const Rml::Matrix4f& matrix) {
const Rml::Matrix4f identity = Rml::Matrix4f::Identity();
const auto* matrixData = matrix.data();
const auto* identityData = identity.data();
for (size_t i = 0; i < 16; ++i) {
if (std::abs(matrixData[i] - identityData[i]) > FilterEpsilon) {
return false;
}
}
return true;
}
bool is_identity_filter(const CompiledFilter& filter) {
switch (filter.type) {
case FilterType::Opacity:
return std::abs(filter.opacity - 1.f) <= FilterEpsilon;
case FilterType::Blur:
return filter.sigma < 0.5f;
case FilterType::ColorMatrix:
return is_identity_matrix(filter.colorMatrix);
case FilterType::DropShadow:
case FilterType::MaskImage:
default:
return false;
}
}
std::vector<Rml::CompiledFilterHandle> active_filters(Rml::Span<const Rml::CompiledFilterHandle> filters) {
std::vector<Rml::CompiledFilterHandle> activeFilters;
activeFilters.reserve(filters.size());
for (Rml::CompiledFilterHandle filterHandle : filters) {
const auto* filter = reinterpret_cast<const CompiledFilter*>(filterHandle);
if (filter != nullptr && !is_identity_filter(*filter)) {
activeFilters.push_back(filterHandle);
}
}
return activeFilters;
}
bool try_fold_simple_filters(Rml::Span<const Rml::CompiledFilterHandle> filters, Rml::Matrix4f& colorMatrix,
float& opacity) {
colorMatrix = Rml::Matrix4f::Identity();
opacity = 1.f;
for (Rml::CompiledFilterHandle filterHandle : filters) {
const auto* filter = reinterpret_cast<const CompiledFilter*>(filterHandle);
if (filter == nullptr) {
continue;
}
switch (filter->type) {
case FilterType::Opacity:
opacity *= filter->opacity;
break;
case FilterType::ColorMatrix:
colorMatrix = filter->colorMatrix * colorMatrix;
break;
case FilterType::Blur:
case FilterType::DropShadow:
case FilterType::MaskImage:
default:
return false;
}
}
return true;
}
Rml::Rectanglei downsample_scissor(Rml::Rectanglei scissor) {
scissor.p0 = (scissor.p0 + Rml::Vector2i(1)) / 2;
scissor.p1 = Rml::Math::Max(scissor.p1 / 2, scissor.p0);
@@ -223,6 +309,18 @@ gfx::PipelineRef blit_pipeline(wgpu::TextureFormat colorFormat, uint32_t sampleC
blend ? BlendMode::Premultiplied : BlendMode::None));
}
gfx::PipelineRef simple_filter_pipeline(wgpu::TextureFormat colorFormat, uint32_t sampleCount,
WebGPURenderInterface::BlitPipelineType type, bool useStencil) {
const bool blend = type == WebGPURenderInterface::BlitPipelineType::Blend ||
type == WebGPURenderInterface::BlitPipelineType::BlendMasked;
const bool masked = type == WebGPURenderInterface::BlitPipelineType::BlendMasked ||
type == WebGPURenderInterface::BlitPipelineType::ReplaceMasked;
return gfx::pipeline_ref(
make_pipeline_config(PipelineKind::SimpleFilter, colorFormat, sampleCount, VertexLayoutKind::Fullscreen,
masked ? StencilMode::EqualKeep : (useStencil ? StencilMode::AlwaysKeep : StencilMode::None),
blend ? BlendMode::Premultiplied : BlendMode::None));
}
gfx::PipelineRef seed_resample_pipeline(wgpu::TextureFormat colorFormat, uint32_t sampleCount, bool useStencil) {
return gfx::pipeline_ref(
make_pipeline_config(PipelineKind::SeedResample, colorFormat, sampleCount, VertexLayoutKind::Fullscreen,
@@ -640,6 +738,11 @@ void WebGPURenderInterface::EnsureFrameRenderingStarted() {
}
m_frameRenderingStarted = true;
if (m_baseLayerContent == BaseLayerContent::Transparent) {
BeginLayerPass(0, wgpu::LoadOp::Clear, "RmlUi transparent base layer pass", true);
return;
}
const auto seedBindGroup = texture_bind_group_ref(m_frameSeedView);
const auto seedUniformRange = gfx::push_uniform(SeedResampleUniformBlock{
.samplerMode = sampler_mode(),
@@ -832,9 +935,8 @@ void WebGPURenderInterface::RenderBlur(float sigma, const RenderTarget& sourceDe
}
}
void WebGPURenderInterface::RenderFilters(Rml::Span<const Rml::CompiledFilterHandle> filters) {
constexpr size_t sourceIndex = 0;
constexpr size_t shadowIndex = 1;
size_t WebGPURenderInterface::RenderFilters(Rml::Span<const Rml::CompiledFilterHandle> filters) {
size_t sourceIndex = 0;
constexpr size_t tempIndex = 2;
for (Rml::CompiledFilterHandle filterHandle : filters) {
@@ -842,22 +944,24 @@ void WebGPURenderInterface::RenderFilters(Rml::Span<const Rml::CompiledFilterHan
if (filter == nullptr) {
continue;
}
const size_t scratchIndex = sourceIndex == 0 ? 1 : 0;
switch (filter->type) {
case FilterType::Opacity: {
BeginRenderTargetPass(m_postprocessTargets[shadowIndex].view, wgpu::LoadOp::Clear, "RmlUi opacity pass");
const SimpleFilterUniformBlock simpleFilterUniform{
.matrix = to_shader_matrix(Rml::Matrix4f::Identity()),
.opacity = {filter->opacity, 0.f, 0.f, 0.f},
};
const auto simpleFilterRange = gfx::push_uniform(simpleFilterUniform);
BeginRenderTargetPass(m_postprocessTargets[scratchIndex].view, wgpu::LoadOp::Clear, "RmlUi opacity pass");
DrawFullscreenTexture(texture_bind_group_ref(m_postprocessTargets[sourceIndex].view),
filter_pipeline(PipelineKind::Opacity, m_renderTargetFormat, VertexLayoutKind::Fullscreen,
BlendMode::Opacity),
0, {}, true, {filter->opacity, filter->opacity, filter->opacity, filter->opacity}, true);
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[shadowIndex].view),
m_postprocessTargets[sourceIndex].view, wgpu::LoadOp::Clear,
blit_pipeline(m_renderTargetFormat, 1, BlitPipelineType::Replace, false),
"RmlUi opacity result copy pass");
filter_pipeline(PipelineKind::SimpleFilter, m_renderTargetFormat), uniform_bind_group_ref(),
simpleFilterRange);
sourceIndex = scratchIndex;
break;
}
case FilterType::Blur:
RenderBlur(filter->sigma, m_postprocessTargets[sourceIndex], m_postprocessTargets[shadowIndex]);
RenderBlur(filter->sigma, m_postprocessTargets[sourceIndex], m_postprocessTargets[tempIndex]);
break;
case FilterType::DropShadow: {
TexCoordLimits texCoordLimits = GetPostprocessTexCoordLimits();
@@ -883,58 +987,52 @@ void WebGPURenderInterface::RenderFilters(Rml::Span<const Rml::CompiledFilterHan
};
const auto dropShadowRange = gfx::push_uniform(dropShadowUniform);
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[sourceIndex].view),
m_postprocessTargets[shadowIndex].view, wgpu::LoadOp::Clear,
m_postprocessTargets[scratchIndex].view, wgpu::LoadOp::Clear,
filter_pipeline(PipelineKind::DropShadow, m_renderTargetFormat), "RmlUi drop shadow pass",
uniform_bind_group_ref(), dropShadowRange);
RenderBlur(filter->sigma, m_postprocessTargets[shadowIndex], m_postprocessTargets[tempIndex]);
RenderBlur(filter->sigma, m_postprocessTargets[scratchIndex], m_postprocessTargets[tempIndex]);
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[sourceIndex].view),
m_postprocessTargets[shadowIndex].view, wgpu::LoadOp::Load,
m_postprocessTargets[scratchIndex].view, wgpu::LoadOp::Load,
blit_pipeline(m_renderTargetFormat, 1, BlitPipelineType::Blend, false),
"RmlUi drop shadow source composite pass");
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[shadowIndex].view),
m_postprocessTargets[sourceIndex].view, wgpu::LoadOp::Clear,
blit_pipeline(m_renderTargetFormat, 1, BlitPipelineType::Replace, false),
"RmlUi drop shadow result copy pass");
sourceIndex = scratchIndex;
break;
}
case FilterType::ColorMatrix: {
const ColorMatrixUniformBlock colorMatrixUniform{
const SimpleFilterUniformBlock simpleFilterUniform{
.matrix = to_shader_matrix(filter->colorMatrix),
.opacity = {1.f, 0.f, 0.f, 0.f},
};
const auto colorMatrixRange = gfx::push_uniform(colorMatrixUniform);
const auto simpleFilterRange = gfx::push_uniform(simpleFilterUniform);
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[sourceIndex].view),
m_postprocessTargets[shadowIndex].view, wgpu::LoadOp::Clear,
filter_pipeline(PipelineKind::ColorMatrix, m_renderTargetFormat), "RmlUi color matrix pass",
uniform_bind_group_ref(), colorMatrixRange);
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[shadowIndex].view),
m_postprocessTargets[sourceIndex].view, wgpu::LoadOp::Clear,
blit_pipeline(m_renderTargetFormat, 1, BlitPipelineType::Replace, false),
"RmlUi color matrix result copy pass");
m_postprocessTargets[scratchIndex].view, wgpu::LoadOp::Clear,
filter_pipeline(PipelineKind::SimpleFilter, m_renderTargetFormat), "RmlUi color matrix pass",
uniform_bind_group_ref(), simpleFilterRange);
sourceIndex = scratchIndex;
break;
}
case FilterType::MaskImage: {
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[sourceIndex].view),
m_postprocessTargets[shadowIndex].view, wgpu::LoadOp::Clear,
m_postprocessTargets[scratchIndex].view, wgpu::LoadOp::Clear,
filter_pipeline(PipelineKind::MaskImage, m_renderTargetFormat), "RmlUi mask image pass",
texture_bind_group_ref(m_blendMaskTarget.view), {}, false);
CompositeToTarget(texture_bind_group_ref(m_postprocessTargets[shadowIndex].view),
m_postprocessTargets[sourceIndex].view, wgpu::LoadOp::Clear,
blit_pipeline(m_renderTargetFormat, 1, BlitPipelineType::Replace, false),
"RmlUi mask image result copy pass");
sourceIndex = scratchIndex;
break;
}
}
}
EndActivePass();
return sourceIndex;
}
void WebGPURenderInterface::BeginFrame(const webgpu::TextureWithSampler& target,
const webgpu::TextureWithSampler& seedTarget) {
m_frameSeedView = seedTarget.view;
const webgpu::TextureWithSampler& sceneTarget,
BaseLayerContent baseLayerContent) {
m_frameSeedView = sceneTarget.view;
m_frameSize = target.size;
m_viewport = {
.left = 0.f,
@@ -950,6 +1048,7 @@ void WebGPURenderInterface::BeginFrame(const webgpu::TextureWithSampler& target,
m_frameRenderingStarted = false;
m_frameActive = true;
m_passActive = false;
m_baseLayerContent = baseLayerContent;
NewFrame();
EnsureFrameTargets(target.size);
@@ -998,6 +1097,7 @@ bool WebGPURenderInterface::EndFrame() {
m_frameActive = false;
m_frameSeedView = {};
m_frameRenderingStarted = false;
m_baseLayerContent = BaseLayerContent::Transparent;
return rendered;
}
@@ -1024,17 +1124,52 @@ void WebGPURenderInterface::CompositeLayers(Rml::LayerHandle source, Rml::LayerH
}
EnsureFrameRenderingStarted();
CompositeToTarget(texture_bind_group_ref(m_layers[source].view), m_postprocessTargets[0].view, wgpu::LoadOp::Clear,
blit_pipeline(m_renderTargetFormat, 1, BlitPipelineType::Replace, false), "RmlUi layer copy pass");
const Rml::LayerHandle topLayer = m_layerStack.empty() ? 0 : m_layerStack.back();
RenderFilters(filters);
const std::vector<Rml::CompiledFilterHandle> activeFilterHandles = active_filters(filters);
const Rml::Span activeFilters{activeFilterHandles.data(), activeFilterHandles.size()};
const bool replace = blendMode == Rml::BlendMode::Replace;
const BlitPipelineType pipelineType =
replace ? (m_clipMaskEnabled ? BlitPipelineType::ReplaceMasked : BlitPipelineType::Replace)
: (m_clipMaskEnabled ? BlitPipelineType::BlendMasked : BlitPipelineType::Blend);
if (activeFilters.empty() && source != destination) {
BeginLayerPass(destination, wgpu::LoadOp::Load, "RmlUi layer direct composite pass");
DrawFullscreenTexture(texture_bind_group_ref(m_layers[source].view),
blit_pipeline(m_renderTargetFormat, LayerSampleCount, pipelineType, true));
if (destination != topLayer) {
EndActivePass();
m_activeLayer = topLayer;
}
return;
}
Rml::Matrix4f simpleFilterMatrix;
float simpleFilterOpacity = 1.f;
if (source != destination && try_fold_simple_filters(activeFilters, simpleFilterMatrix, simpleFilterOpacity)) {
const SimpleFilterUniformBlock simpleFilterUniform{
.matrix = to_shader_matrix(simpleFilterMatrix),
.opacity = {simpleFilterOpacity, 0.f, 0.f, 0.f},
};
const auto simpleFilterRange = gfx::push_uniform(simpleFilterUniform);
BeginLayerPass(destination, wgpu::LoadOp::Load, "RmlUi simple filter composite pass");
DrawFullscreenTexture(texture_bind_group_ref(m_layers[source].view),
simple_filter_pipeline(m_renderTargetFormat, LayerSampleCount, pipelineType, true),
uniform_bind_group_ref(), simpleFilterRange);
if (destination != topLayer) {
EndActivePass();
m_activeLayer = topLayer;
}
return;
}
CompositeToTarget(texture_bind_group_ref(m_layers[source].view), m_postprocessTargets[0].view, wgpu::LoadOp::Clear,
blit_pipeline(m_renderTargetFormat, 1, BlitPipelineType::Replace, false), "RmlUi layer copy pass");
const size_t filteredIndex = RenderFilters(activeFilters);
BeginLayerPass(destination, wgpu::LoadOp::Load, "RmlUi layer composite pass");
DrawFullscreenTexture(texture_bind_group_ref(m_postprocessTargets[0].view),
DrawFullscreenTexture(texture_bind_group_ref(m_postprocessTargets[filteredIndex].view),
blit_pipeline(m_renderTargetFormat, LayerSampleCount, pipelineType, true));
if (destination != topLayer) {
+12 -21
View File
@@ -41,8 +41,9 @@ struct DropShadowUniformBlock {
Rml::Vector2f texCoordMax;
};
struct ColorMatrixUniformBlock {
struct SimpleFilterUniformBlock {
Rml::ColumnMajorMatrix4f matrix;
Rml::Vector4f opacity;
};
struct GradientUniformBlock {
@@ -60,6 +61,11 @@ struct TexCoordLimits {
Rml::Vector2f max;
};
enum class BaseLayerContent {
Transparent,
Scene,
};
class WebGPURenderInterface : public Rml::RenderInterface {
public:
static constexpr wgpu::TextureFormat ClipMaskStencilFormat = wgpu::TextureFormat::Stencil8;
@@ -81,14 +87,6 @@ public:
};
private:
enum class FilterType {
Opacity,
Blur,
DropShadow,
ColorMatrix,
MaskImage,
};
struct RenderTarget {
wgpu::Texture texture;
wgpu::TextureView view;
@@ -97,15 +95,6 @@ private:
wgpu::Extent3D size;
};
struct CompiledFilter {
FilterType type = FilterType::Blur;
float opacity = 1.f;
float sigma = 0.f;
Rml::Vector2f offset;
Rml::ColourbPremultiplied color;
Rml::Matrix4f colorMatrix;
};
wgpu::TextureView m_frameSeedView;
wgpu::TextureFormat m_renderTargetFormat = wgpu::TextureFormat::Undefined;
@@ -120,10 +109,11 @@ private:
Rml::Vector2i m_clipResetGeometrySize{};
std::vector<RenderTarget> m_layers;
std::array<RenderTarget, 3> m_postprocessTargets{};
RenderTarget m_blendMaskTarget;
RenderTarget m_blendMaskTarget{};
std::vector<Rml::LayerHandle> m_layerStack;
Rml::LayerHandle m_activeLayer = 0;
Rml::LayerHandle m_nextLayer = 1;
BaseLayerContent m_baseLayerContent = BaseLayerContent::Transparent;
Rml::Vector2i m_windowSize{};
Rml::Matrix4f m_translationMatrix = Rml::Matrix4f::Identity();
@@ -169,7 +159,7 @@ private:
gfx::Range extraUniformRange = {}, bool extraBindGroupHasDynamicOffset = true,
std::array<float, 4> blendConstant = {}, bool hasBlendConstant = false);
void RenderBlur(float sigma, const RenderTarget& sourceDestination, const RenderTarget& temp);
void RenderFilters(Rml::Span<const Rml::CompiledFilterHandle> filters);
size_t RenderFilters(Rml::Span<const Rml::CompiledFilterHandle> filters);
public:
Rml::CompiledGeometryHandle CompileGeometry(Rml::Span<const Rml::Vertex> vertices,
@@ -199,7 +189,8 @@ public:
Rml::TextureHandle texture) override;
void ReleaseShader(Rml::CompiledShaderHandle shader) override;
void BeginFrame(const webgpu::TextureWithSampler& target, const webgpu::TextureWithSampler& seed_target);
void BeginFrame(const webgpu::TextureWithSampler& target, const webgpu::TextureWithSampler& scene_target,
BaseLayerContent baseLayerContent);
bool EndFrame();
void SetWindowSize(const Rml::Vector2i& window_size) { m_windowSize = window_size; }
void SetRenderTargetFormat(wgpu::TextureFormat render_target_format) { m_renderTargetFormat = render_target_format; }
+11 -27
View File
@@ -24,8 +24,8 @@ constexpr uint32_t DynamicGroup2 = 1u << 2u;
constexpr uint64_t CommonUniformBindingSize = AURORA_ALIGN(sizeof(UniformBlock), 16);
constexpr uint64_t ExtraUniformBindingSize =
AURORA_ALIGN(std::max({sizeof(BlurUniformBlock), sizeof(DropShadowUniformBlock), sizeof(ColorMatrixUniformBlock),
sizeof(GradientUniformBlock), sizeof(SeedResampleUniformBlock)}),
AURORA_ALIGN(std::max({sizeof(BlurUniformBlock), sizeof(DropShadowUniformBlock), sizeof(SimpleFilterUniformBlock),
sizeof(GradientUniforzxmBlock), sizeof(SeedResampleUniformBlock)}),
16);
constexpr std::string_view vertexSource = R"(
@@ -367,20 +367,21 @@ fn main(@builtin(position) position: vec4<f32>, @location(0) uv: vec2<f32>) -> @
}
)"sv;
constexpr std::string_view colorMatrixFragmentSource = R"(
struct ColorMatrixUniforms {
constexpr std::string_view simpleFilterFragmentSource = R"(
struct SimpleFilterUniforms {
matrix: mat4x4<f32>,
opacity: vec4<f32>,
};
@group(0) @binding(1) var s: sampler;
@group(1) @binding(0) var t: texture_2d<f32>;
@group(2) @binding(0) var<uniform> color_matrix: ColorMatrixUniforms;
@group(2) @binding(0) var<uniform> simple_filter: SimpleFilterUniforms;
@fragment
fn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let tex_color = textureSample(t, s, uv);
let transformed_color = (color_matrix.matrix * tex_color).rgb;
return vec4<f32>(transformed_color, tex_color.a);
let transformed_color = simple_filter.matrix * tex_color;
return vec4<f32>(transformed_color.rgb, tex_color.a) * simple_filter.opacity.x;
}
)"sv;
@@ -511,21 +512,6 @@ wgpu::BlendState blend_state(BlendMode mode) {
.dstFactor = wgpu::BlendFactor::OneMinusSrcAlpha,
},
};
case BlendMode::Opacity:
return {
.color =
{
.operation = wgpu::BlendOperation::Add,
.srcFactor = wgpu::BlendFactor::Constant,
.dstFactor = wgpu::BlendFactor::Zero,
},
.alpha =
{
.operation = wgpu::BlendOperation::Add,
.srcFactor = wgpu::BlendFactor::Constant,
.dstFactor = wgpu::BlendFactor::Zero,
},
};
case BlendMode::None:
default:
return {};
@@ -548,7 +534,7 @@ const wgpu::PipelineLayout create_pipeline_layout(PipelineKind kind) {
case PipelineKind::Blur:
case PipelineKind::RegionBlit:
case PipelineKind::DropShadow:
case PipelineKind::ColorMatrix:
case PipelineKind::SimpleFilter:
case PipelineKind::SeedResample:
layouts[layoutCount++] = g_imageBindGroupLayout;
layouts[layoutCount++] = g_uniformBindGroupLayout;
@@ -556,7 +542,6 @@ const wgpu::PipelineLayout create_pipeline_layout(PipelineKind kind) {
case PipelineKind::Geometry:
case PipelineKind::Blit:
case PipelineKind::OpaqueBlit:
case PipelineKind::Opacity:
default:
layouts[layoutCount++] = g_imageBindGroupLayout;
break;
@@ -585,12 +570,11 @@ const std::string_view fragment_source(PipelineKind kind) {
return regionBlitFragmentSource;
case PipelineKind::DropShadow:
return dropShadowFragmentSource;
case PipelineKind::ColorMatrix:
return colorMatrixFragmentSource;
case PipelineKind::SimpleFilter:
return simpleFilterFragmentSource;
case PipelineKind::MaskImage:
return maskImageFragmentSource;
case PipelineKind::Blit:
case PipelineKind::Opacity:
default:
return blitFragmentSource;
}
+2 -4
View File
@@ -10,7 +10,7 @@
namespace aurora::rmlui {
constexpr uint32_t RmlPipelineConfigVersion = 1;
constexpr uint32_t RmlPipelineConfigVersion = 2;
enum class PipelineKind : uint32_t {
Geometry,
@@ -18,11 +18,10 @@ enum class PipelineKind : uint32_t {
Blit,
OpaqueBlit,
SeedResample,
Opacity,
SimpleFilter,
Blur,
RegionBlit,
DropShadow,
ColorMatrix,
MaskImage,
};
@@ -43,7 +42,6 @@ enum class StencilMode : uint32_t {
enum class BlendMode : uint32_t {
None,
Premultiplied,
Opacity,
};
enum class DrawKind : uint32_t {
+59 -29
View File
@@ -47,6 +47,7 @@ TextureWithSampler g_depthBuffer;
// EFB -> XFB copy pipeline
static wgpu::BindGroupLayout g_CopyBindGroupLayout;
wgpu::RenderPipeline g_CopyPipeline;
wgpu::RenderPipeline g_CopyPremultipliedAlphaPipeline;
wgpu::BindGroup g_CopyBindGroup;
static AuroraSampler g_Resampler = SAMPLER_BILINEAR;
static wgpu::BindGroupLayout g_ResampleBindGroupLayout;
@@ -411,26 +412,21 @@ fn vs_main(@builtin(vertex_index) vtxIdx: u32) -> VertexOutput {
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
fn fs_opaque(in: VertexOutput) -> @location(0) vec4<f32> {
let color = textureSample(efb_texture, efb_sampler, in.uv);
return vec4(color.rgb, 1.0);
}
@fragment
fn fs_premultiplied_alpha(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(efb_texture, efb_sampler, in.uv);
}
)""";
const wgpu::ShaderModuleDescriptor moduleDescriptor{
.nextInChain = &sourceDescriptor,
.label = "XFB Copy Module",
};
auto module = g_device.CreateShaderModule(&moduleDescriptor);
const std::array colorTargets{wgpu::ColorTargetState{
.format = g_graphicsConfig.surfaceConfiguration.format,
.writeMask = wgpu::ColorWriteMask::All,
}};
const wgpu::FragmentState fragmentState{
.module = module,
.entryPoint = "fs_main",
.targetCount = colorTargets.size(),
.targets = colorTargets.data(),
};
const std::array bindGroupLayoutEntries{
wgpu::BindGroupLayoutEntry{
.binding = 0,
@@ -460,25 +456,58 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
.bindGroupLayouts = &g_CopyBindGroupLayout,
};
auto pipelineLayout = g_device.CreatePipelineLayout(&layoutDescriptor);
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
.layout = pipelineLayout,
.vertex =
wgpu::VertexState{
.module = module,
.entryPoint = "vs_main",
},
.primitive =
wgpu::PrimitiveState{
.topology = wgpu::PrimitiveTopology::TriangleList,
},
.multisample =
wgpu::MultisampleState{
.count = 1,
.mask = UINT32_MAX,
},
.fragment = &fragmentState,
const auto make_copy_pipeline = [&](const char* label, const char* fragmentEntryPoint, const wgpu::BlendState* blend) {
const std::array colorTargets{wgpu::ColorTargetState{
.format = g_graphicsConfig.surfaceConfiguration.format,
.blend = blend,
.writeMask = wgpu::ColorWriteMask::All,
}};
const wgpu::FragmentState fragmentState{
.module = module,
.entryPoint = fragmentEntryPoint,
.targetCount = colorTargets.size(),
.targets = colorTargets.data(),
};
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
.label = label,
.layout = pipelineLayout,
.vertex =
wgpu::VertexState{
.module = module,
.entryPoint = "vs_main",
},
.primitive =
wgpu::PrimitiveState{
.topology = wgpu::PrimitiveTopology::TriangleList,
},
.multisample =
wgpu::MultisampleState{
.count = 1,
.mask = UINT32_MAX,
},
.fragment = &fragmentState,
};
return g_device.CreateRenderPipeline(&pipelineDescriptor);
};
g_CopyPipeline = g_device.CreateRenderPipeline(&pipelineDescriptor);
g_CopyPipeline = make_copy_pipeline("XFB Copy Pipeline", "fs_opaque", nullptr);
const wgpu::BlendState premultipliedAlphaBlend{
.color =
{
.operation = wgpu::BlendOperation::Add,
.srcFactor = wgpu::BlendFactor::One,
.dstFactor = wgpu::BlendFactor::OneMinusSrcAlpha,
},
.alpha =
{
.operation = wgpu::BlendOperation::Add,
.srcFactor = wgpu::BlendFactor::One,
.dstFactor = wgpu::BlendFactor::OneMinusSrcAlpha,
},
};
g_CopyPremultipliedAlphaPipeline =
make_copy_pipeline("XFB Premultiplied Alpha Copy Pipeline", "fs_premultiplied_alpha", &premultipliedAlphaBlend);
}
void create_resample_pipeline() {
@@ -983,6 +1012,7 @@ void shutdown() {
gpu_prof::shutdown();
g_CopyBindGroupLayout = {};
g_CopyPipeline = {};
g_CopyPremultipliedAlphaPipeline = {};
g_CopyBindGroup = {};
g_ResampleBindGroupLayout = {};
g_ResamplePipeline = {};
+1
View File
@@ -48,6 +48,7 @@ extern TextureWithSampler g_frameBuffer;
extern TextureWithSampler g_frameBufferResolved;
extern TextureWithSampler g_depthBuffer;
extern wgpu::RenderPipeline g_CopyPipeline;
extern wgpu::RenderPipeline g_CopyPremultipliedAlphaPipeline;
extern wgpu::BindGroup g_CopyBindGroup;
extern wgpu::Instance g_instance;
extern wgpu::AdapterInfo g_adapterInfo;