mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
fix(vk): Make the color ramp pass interruptible (#11270) ba953a142b
Color ramps are the final resource texture we need to make interruptible for old Android GPUs that don't support complex render passes. Also fix lots_of_tess_spans to look the same on MSAA and not. Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
bc6f965d1f8c5e298073dc65b6298806baaaa9b9
|
||||
ba953a142ba3703152a81b89da93ea77ecb30d57
|
||||
|
||||
@@ -280,10 +280,17 @@ private:
|
||||
|
||||
// Renders color ramps to the gradient texture.
|
||||
class RenderContextVulkanImpl::ColorRampPipeline
|
||||
: public ResourceTexturePipeline
|
||||
{
|
||||
public:
|
||||
ColorRampPipeline(PipelineManagerVulkan* pipelineManager) :
|
||||
m_vk(ref_rcp(pipelineManager->vulkanContext()))
|
||||
ColorRampPipeline(PipelineManagerVulkan* pipelineManager,
|
||||
const DriverWorkarounds& workarounds) :
|
||||
ResourceTexturePipeline(ref_rcp(pipelineManager->vulkanContext()),
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
"ColorRamp",
|
||||
workarounds)
|
||||
{
|
||||
VkDescriptorSetLayout perFlushDescriptorSetLayout =
|
||||
pipelineManager->perFlushDescriptorSetLayout();
|
||||
@@ -359,53 +366,6 @@ public:
|
||||
.pVertexAttributeDescriptions = &vertexAttributeDescription,
|
||||
};
|
||||
|
||||
VkAttachmentDescription attachment = {
|
||||
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
};
|
||||
|
||||
VkSubpassDependency dependencies[] = {
|
||||
// Wait for previous accesses to complete before this renderpass
|
||||
// starts
|
||||
{
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
},
|
||||
{
|
||||
.srcSubpass = 0,
|
||||
.dstSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
},
|
||||
};
|
||||
VkRenderPassCreateInfo renderPassCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &attachment,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &layout::SINGLE_ATTACHMENT_SUBPASS,
|
||||
.dependencyCount = std::size(dependencies),
|
||||
.pDependencies = dependencies,
|
||||
};
|
||||
|
||||
VK_CHECK(m_vk->CreateRenderPass(m_vk->device,
|
||||
&renderPassCreateInfo,
|
||||
nullptr,
|
||||
&m_renderPass));
|
||||
m_vk->setDebugNameIfEnabled(uint64_t(m_renderPass),
|
||||
VK_OBJECT_TYPE_RENDER_PASS,
|
||||
"Color Ramp RenderPass");
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = 2,
|
||||
@@ -418,7 +378,7 @@ public:
|
||||
.pColorBlendState = &layout::SINGLE_ATTACHMENT_BLEND_DISABLED,
|
||||
.pDynamicState = &layout::DYNAMIC_VIEWPORT_SCISSOR,
|
||||
.layout = m_pipelineLayout,
|
||||
.renderPass = m_renderPass,
|
||||
.renderPass = renderPass(),
|
||||
};
|
||||
|
||||
VK_CHECK(m_vk->CreateGraphicsPipelines(m_vk->device,
|
||||
@@ -438,18 +398,14 @@ public:
|
||||
~ColorRampPipeline()
|
||||
{
|
||||
m_vk->DestroyPipelineLayout(m_vk->device, m_pipelineLayout, nullptr);
|
||||
m_vk->DestroyRenderPass(m_vk->device, m_renderPass, nullptr);
|
||||
m_vk->DestroyPipeline(m_vk->device, m_renderPipeline, nullptr);
|
||||
}
|
||||
|
||||
VkPipelineLayout pipelineLayout() const { return m_pipelineLayout; }
|
||||
VkRenderPass renderPass() const { return m_renderPass; }
|
||||
VkPipeline renderPipeline() const { return m_renderPipeline; }
|
||||
|
||||
private:
|
||||
rcp<VulkanContext> m_vk;
|
||||
VkPipelineLayout m_pipelineLayout;
|
||||
VkRenderPass m_renderPass;
|
||||
VkPipeline m_renderPipeline;
|
||||
};
|
||||
|
||||
@@ -936,7 +892,8 @@ void RenderContextVulkanImpl::initGPUObjects(
|
||||
|
||||
// The pipelines reference our vulkan objects. Delete them first.
|
||||
m_colorRampPipeline =
|
||||
std::make_unique<ColorRampPipeline>(m_pipelineManager.get());
|
||||
std::make_unique<ColorRampPipeline>(m_pipelineManager.get(),
|
||||
m_workarounds);
|
||||
m_tessellatePipeline =
|
||||
std::make_unique<TessellatePipeline>(m_pipelineManager.get(),
|
||||
m_workarounds);
|
||||
@@ -1910,20 +1867,9 @@ void RenderContextVulkanImpl::flush(const FlushDescriptor& desc)
|
||||
.extent = {gpu::kGradTextureWidth, desc.gradDataHeight},
|
||||
};
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = m_colorRampPipeline->renderPass(),
|
||||
.framebuffer = *m_gradTextureFramebuffer,
|
||||
.renderArea = renderArea,
|
||||
};
|
||||
|
||||
m_vk->CmdBeginRenderPass(commandBuffer,
|
||||
&renderPassBeginInfo,
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
m_vk->CmdBindPipeline(commandBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_colorRampPipeline->renderPipeline());
|
||||
m_colorRampPipeline->beginRenderPass(commandBuffer,
|
||||
renderArea,
|
||||
*m_gradTextureFramebuffer);
|
||||
|
||||
m_vk->CmdSetViewport(commandBuffer,
|
||||
0,
|
||||
@@ -1950,11 +1896,28 @@ void RenderContextVulkanImpl::flush(const FlushDescriptor& desc)
|
||||
1,
|
||||
ZERO_OFFSET_32);
|
||||
|
||||
m_vk->CmdDraw(commandBuffer,
|
||||
gpu::GRAD_SPAN_TRI_STRIP_VERTEX_COUNT,
|
||||
desc.gradSpanCount,
|
||||
0,
|
||||
0);
|
||||
m_vk->CmdBindPipeline(commandBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_colorRampPipeline->renderPipeline());
|
||||
|
||||
for (auto [chunkInstanceCount, chunkFirstInstance] :
|
||||
InstanceChunker(desc.gradSpanCount,
|
||||
0,
|
||||
m_workarounds.maxInstancesPerRenderPass))
|
||||
|
||||
{
|
||||
m_colorRampPipeline->interruptRenderPassIfNeeded(
|
||||
commandBuffer,
|
||||
renderArea,
|
||||
*m_gradTextureFramebuffer,
|
||||
chunkInstanceCount,
|
||||
m_workarounds);
|
||||
m_vk->CmdDraw(commandBuffer,
|
||||
gpu::GRAD_SPAN_TRI_STRIP_VERTEX_COUNT,
|
||||
chunkInstanceCount,
|
||||
0,
|
||||
chunkFirstInstance);
|
||||
}
|
||||
|
||||
m_vk->CmdEndRenderPass(commandBuffer);
|
||||
|
||||
@@ -3243,7 +3206,8 @@ void RenderContextVulkanImpl::hotloadShaders(
|
||||
|
||||
// Delete and replace old shaders
|
||||
m_colorRampPipeline =
|
||||
std::make_unique<ColorRampPipeline>(m_pipelineManager.get());
|
||||
std::make_unique<ColorRampPipeline>(m_pipelineManager.get(),
|
||||
m_workarounds);
|
||||
m_tessellatePipeline =
|
||||
std::make_unique<TessellatePipeline>(m_pipelineManager.get(),
|
||||
m_workarounds);
|
||||
|
||||
@@ -134,21 +134,20 @@ public:
|
||||
size_t i,
|
||||
size_t j) override
|
||||
{
|
||||
float stops[7] = {0, .147f, .148f, .23f, .67f, .8f, 1};
|
||||
rive::ColorInt colors[7] = {randColor(),
|
||||
randColor(),
|
||||
randColor(),
|
||||
randColor(),
|
||||
randColor(),
|
||||
randColor(),
|
||||
randColor()};
|
||||
rive::ColorInt colors[13];
|
||||
float stops[std::size(colors)];
|
||||
for (size_t i = 0; i < std::size(colors); ++i)
|
||||
{
|
||||
colors[i] = randColor();
|
||||
stops[i] = static_cast<float>(i) / (std::size(stops) - 1);
|
||||
}
|
||||
return factory->makeLinearGradient((i & 1) ? 16 : 0,
|
||||
(j & 1) ? 16 : 0,
|
||||
(i & 1) ? 0 : 16,
|
||||
(j & 1) ? 0 : 16,
|
||||
colors,
|
||||
stops,
|
||||
7);
|
||||
std::size(colors));
|
||||
}
|
||||
};
|
||||
GMREGISTER(lots_of_grad_spans, return new LotsOfGradSpansGM)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "gm.hpp"
|
||||
#include "gmutils.hpp"
|
||||
#include "rive/renderer.hpp"
|
||||
#include <set>
|
||||
|
||||
using namespace rivegm;
|
||||
using namespace rive;
|
||||
@@ -20,8 +21,10 @@ static void add_circle(PathBuilder&,
|
||||
static void draw_spirograph(Renderer*,
|
||||
RenderPaint*,
|
||||
RenderPath* circles,
|
||||
float2 translate,
|
||||
float2 c,
|
||||
int depth);
|
||||
int depth,
|
||||
std::set<std::tuple<int, int>>* drawnCenters);
|
||||
|
||||
// Stress tests the renderer by trying to fit as many tessellation spans into a
|
||||
// single flush as possible. This will hopefully detect any potential driver
|
||||
@@ -52,9 +55,7 @@ private:
|
||||
}
|
||||
Path circles = pathBuilder.detach();
|
||||
|
||||
renderer->save();
|
||||
|
||||
renderer->translate(512, 512);
|
||||
float2 translate = {512, 512};
|
||||
Paint paint;
|
||||
paint->style(m_style);
|
||||
// Use round joins because they use fewer segments when nearly flat, and
|
||||
@@ -64,24 +65,35 @@ private:
|
||||
paint->color((m_style == RenderPaintStyle::fill) ? 0xff7500ff
|
||||
: 0xfffe0089);
|
||||
renderer->drawPath(circles, paint);
|
||||
|
||||
renderer->save();
|
||||
draw_spirograph(renderer, paint, circles, float2{-R, 0}, 3);
|
||||
renderer->restore();
|
||||
|
||||
renderer->save();
|
||||
draw_spirograph(renderer, paint, circles, float2{0, R}, 3);
|
||||
renderer->restore();
|
||||
|
||||
renderer->save();
|
||||
draw_spirograph(renderer, paint, circles, float2{R, 0}, 3);
|
||||
renderer->restore();
|
||||
|
||||
renderer->save();
|
||||
draw_spirograph(renderer, paint, circles, float2{0, -R}, 3);
|
||||
renderer->restore();
|
||||
|
||||
renderer->restore();
|
||||
std::set<std::tuple<int, int>> drawnCenters;
|
||||
draw_spirograph(renderer,
|
||||
paint,
|
||||
circles,
|
||||
translate,
|
||||
float2{-R, 0},
|
||||
3,
|
||||
&drawnCenters);
|
||||
draw_spirograph(renderer,
|
||||
paint,
|
||||
circles,
|
||||
translate,
|
||||
float2{0, R},
|
||||
3,
|
||||
&drawnCenters);
|
||||
draw_spirograph(renderer,
|
||||
paint,
|
||||
circles,
|
||||
translate,
|
||||
float2{R, 0},
|
||||
3,
|
||||
&drawnCenters);
|
||||
draw_spirograph(renderer,
|
||||
paint,
|
||||
circles,
|
||||
translate,
|
||||
float2{0, -R},
|
||||
3,
|
||||
&drawnCenters);
|
||||
}
|
||||
|
||||
const RenderPaintStyle m_style;
|
||||
@@ -124,16 +136,28 @@ static void add_circle(PathBuilder& pathBuilder,
|
||||
static void draw_spirograph(Renderer* renderer,
|
||||
RenderPaint* paint,
|
||||
RenderPath* circles,
|
||||
float2 translate,
|
||||
float2 c,
|
||||
int depth)
|
||||
int depth,
|
||||
std::set<std::tuple<int, int>>* drawnCenters)
|
||||
{
|
||||
if (depth <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderer->translate(c.x, c.y);
|
||||
renderer->drawPath(circles, paint);
|
||||
translate += c;
|
||||
|
||||
// Only draw the circles once, centered within any given pixel. When we draw
|
||||
// overlapping circles, coverage-based AA darkens but MSAA does not, leading
|
||||
// to divergence in golden images.
|
||||
int2 icenter = simd::cast<int>(simd::floor(translate + .5f));
|
||||
if (drawnCenters->insert({int(icenter.x), int(icenter.y)}).second)
|
||||
{
|
||||
AutoRestore ar(renderer, /*doSave=*/true);
|
||||
renderer->translate(translate.x, translate.y);
|
||||
renderer->drawPath(circles, paint);
|
||||
}
|
||||
|
||||
// Next draw circles centered on both points of intersection between the
|
||||
// circle we just drew and the one before it!
|
||||
@@ -166,8 +190,20 @@ static void draw_spirograph(Renderer* renderer,
|
||||
right = right.yx;
|
||||
}
|
||||
|
||||
draw_spirograph(renderer, paint, circles, left, depth - 1);
|
||||
draw_spirograph(renderer, paint, circles, right, depth - 1);
|
||||
draw_spirograph(renderer,
|
||||
paint,
|
||||
circles,
|
||||
translate,
|
||||
left,
|
||||
depth - 1,
|
||||
drawnCenters);
|
||||
draw_spirograph(renderer,
|
||||
paint,
|
||||
circles,
|
||||
translate,
|
||||
right,
|
||||
depth - 1,
|
||||
drawnCenters);
|
||||
}
|
||||
|
||||
GMREGISTER(lots_of_tess_spans_stroke,
|
||||
|
||||
Reference in New Issue
Block a user