mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Optimize RmlUi render passes
This commit is contained in:
+11
-3
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 = {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user