blob: 1bdf529fc48aae7245ddd5eb9bb0f5ef54fdb648 [file] [log] [blame]
/*
* Copyright 2023 Rive
*/
#include "rive/renderer/d3d11/render_context_d3d_impl.hpp"
#include "rive/renderer/d3d/d3d_constants.hpp"
#include "rive/renderer/texture.hpp"
#include <D3DCompiler.h>
#include "generated/shaders/tessellate.glsl.exports.h"
// offline shaders
namespace shader
{
namespace tess::vert
{
#include "generated/shaders/d3d/tessellate.vert.h"
}
namespace tess::frag
{
#include "generated/shaders/d3d/tessellate.frag.h"
}
namespace grad::vert
{
#include "generated/shaders/d3d/color_ramp.vert.h"
}
namespace grad::frag
{
#include "generated/shaders/d3d/color_ramp.frag.h"
}
namespace atlas::vert
{
#include "generated/shaders/d3d/render_atlas.vert.h"
}
namespace atlas::fill
{
#include "generated/shaders/d3d/render_atlas_fill.frag.h"
}
namespace atlas::stroke
{
#include "generated/shaders/d3d/render_atlas_stroke.frag.h"
}
} // namespace shader
namespace rive::gpu
{
ComPtr<ID3D11Texture2D> make_simple_2d_texture(ID3D11Device* gpu,
DXGI_FORMAT format,
UINT width,
UINT height,
UINT mipLevelCount,
UINT bindFlags,
UINT miscFlags = 0)
{
D3D11_TEXTURE2D_DESC desc{};
desc.Width = width;
desc.Height = height;
desc.MipLevels = mipLevelCount;
desc.ArraySize = 1;
desc.Format = format;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = bindFlags;
desc.CPUAccessFlags = 0;
desc.MiscFlags = miscFlags;
ComPtr<ID3D11Texture2D> tex;
VERIFY_OK(gpu->CreateTexture2D(&desc, NULL, tex.ReleaseAndGetAddressOf()));
return tex;
}
static ComPtr<ID3D11UnorderedAccessView> make_simple_2d_uav(
ID3D11Device* gpu,
ID3D11Texture2D* tex,
DXGI_FORMAT format)
{
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc{};
uavDesc.Format = format;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
ComPtr<ID3D11UnorderedAccessView> uav;
VERIFY_OK(gpu->CreateUnorderedAccessView(tex,
&uavDesc,
uav.ReleaseAndGetAddressOf()));
return uav;
}
D3D11PipelineManager::D3D11PipelineManager(
ComPtr<ID3D11DeviceContext> context,
ComPtr<ID3D11Device> device,
const D3DCapabilities& capabilities,
ShaderCompilationMode shaderCompilationMode) :
Super(device, capabilities, shaderCompilationMode, "vs_5_0", "ps_5_0"),
m_context(context)
{
D3D11_INPUT_ELEMENT_DESC spanDesc = {GLSL_a_span,
0,
DXGI_FORMAT_R32G32B32A32_UINT,
0,
0,
D3D11_INPUT_PER_INSTANCE_DATA,
1};
VERIFY_OK(
this->device()->CreateInputLayout(&spanDesc,
1,
shader::grad::vert::g_main,
std::size(shader::grad::vert::g_main),
&m_colorRampLayout));
VERIFY_OK(this->device()->CreateVertexShader(
shader::grad::vert::g_main,
std::size(shader::grad::vert::g_main),
nullptr,
&m_colorRampVertexShader));
VERIFY_OK(
this->device()->CreatePixelShader(shader::grad::frag::g_main,
std::size(shader::grad::frag::g_main),
nullptr,
&m_colorRampPixelShader));
D3D11_INPUT_ELEMENT_DESC attribsDesc[] = {{GLSL_a_p0p1_,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
0,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_INSTANCE_DATA,
1},
{GLSL_a_p2p3_,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
0,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_INSTANCE_DATA,
1},
{GLSL_a_joinTan_and_ys,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
0,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_INSTANCE_DATA,
1},
{GLSL_a_args,
0,
DXGI_FORMAT_R32G32B32A32_UINT,
0,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_INSTANCE_DATA,
1}};
VERIFY_OK(
this->device()->CreateInputLayout(attribsDesc,
std::size(attribsDesc),
shader::tess::vert::g_main,
std::size(shader::tess::vert::g_main),
&m_tessellateLayout));
VERIFY_OK(this->device()->CreateVertexShader(
shader::tess::vert::g_main,
std::size(shader::tess::vert::g_main),
nullptr,
&m_tessellateVertexShader));
VERIFY_OK(
this->device()->CreatePixelShader(shader::tess::frag::g_main,
std::size(shader::tess::frag::g_main),
nullptr,
&m_tessellatePixelShader));
D3D11_INPUT_ELEMENT_DESC layoutDesc[2];
layoutDesc[0] = {GLSL_a_patchVertexData,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
PATCH_VERTEX_DATA_SLOT,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA,
0};
layoutDesc[1] = {GLSL_a_mirroredVertexData,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
PATCH_VERTEX_DATA_SLOT,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA,
0};
VERIFY_OK(this->device()->CreateInputLayout(
layoutDesc,
2,
shader::atlas::vert::g_main,
std::size(shader::atlas::vert::g_main),
&m_atlasLayout));
VERIFY_OK(this->device()->CreateVertexShader(
shader::atlas::vert::g_main,
std::size(shader::atlas::vert::g_main),
nullptr,
&m_atlasVertexShader));
VERIFY_OK(this->device()->CreatePixelShader(
shader::atlas::fill::g_main,
std::size(shader::atlas::fill::g_main),
nullptr,
&m_atlasFillPixelShader));
VERIFY_OK(this->device()->CreatePixelShader(
shader::atlas::stroke::g_main,
std::size(shader::atlas::stroke::g_main),
nullptr,
&m_atlasStrokePixelShader));
}
bool D3D11PipelineManager::setPipelineState(
DrawType drawType,
ShaderFeatures features,
InterlockMode interlockMode,
ShaderMiscFlags miscFlags,
const PlatformFeatures& platformFeatures
#ifdef WITH_RIVE_TOOLS
,
SynthesizedFailureType synthesizedFailureType
#endif
)
{
auto* pipeline = tryGetPipeline(
{
.drawType = drawType,
.shaderFeatures = features,
.interlockMode = interlockMode,
.shaderMiscFlags = miscFlags,
#ifdef WITH_RIVE_TOOLS
.synthesizedFailureType = synthesizedFailureType,
#endif
},
platformFeatures);
if (pipeline == nullptr)
{
return false;
}
m_context->IASetInputLayout(pipeline->m_vertexShader.layout.Get());
m_context->VSSetShader(pipeline->m_vertexShader.shader.Get(), nullptr, 0);
m_context->PSSetShader(pipeline->m_pixelShader.shader.Get(), nullptr, 0);
return true;
}
std::unique_ptr<D3D11DrawVertexShader> D3D11PipelineManager::
compileVertexShaderBlobToFinalType(DrawType drawType, ComPtr<ID3DBlob> blob)
{
D3D11_INPUT_ELEMENT_DESC layoutDesc[2];
uint32_t vertexAttribCount;
switch (drawType)
{
case DrawType::midpointFanPatches:
case DrawType::midpointFanCenterAAPatches:
case DrawType::outerCurvePatches:
layoutDesc[0] = {GLSL_a_patchVertexData,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
PATCH_VERTEX_DATA_SLOT,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA,
0};
layoutDesc[1] = {GLSL_a_mirroredVertexData,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
PATCH_VERTEX_DATA_SLOT,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA,
0};
vertexAttribCount = 2;
break;
case DrawType::interiorTriangulation:
case DrawType::atlasBlit:
layoutDesc[0] = {GLSL_a_triangleVertex,
0,
DXGI_FORMAT_R32G32B32_FLOAT,
TRIANGLE_VERTEX_DATA_SLOT,
0,
D3D11_INPUT_PER_VERTEX_DATA,
0};
vertexAttribCount = 1;
break;
case DrawType::imageRect:
layoutDesc[0] = {GLSL_a_imageRectVertex,
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
IMAGE_RECT_VERTEX_DATA_SLOT,
0,
D3D11_INPUT_PER_VERTEX_DATA,
0};
vertexAttribCount = 1;
break;
case DrawType::imageMesh:
layoutDesc[0] = {GLSL_a_position,
0,
DXGI_FORMAT_R32G32_FLOAT,
IMAGE_MESH_VERTEX_DATA_SLOT,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA,
0};
layoutDesc[1] = {GLSL_a_texCoord,
0,
DXGI_FORMAT_R32G32_FLOAT,
IMAGE_MESH_UV_DATA_SLOT,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA,
0};
vertexAttribCount = 2;
break;
case DrawType::renderPassResolve:
vertexAttribCount = 0;
break;
case DrawType::msaaStrokes:
case DrawType::msaaMidpointFanBorrowedCoverage:
case DrawType::msaaMidpointFans:
case DrawType::msaaMidpointFanStencilReset:
case DrawType::msaaMidpointFanPathsStencil:
case DrawType::msaaMidpointFanPathsCover:
case DrawType::msaaOuterCubics:
case DrawType::msaaStencilClipReset:
case DrawType::renderPassInitialize:
RIVE_UNREACHABLE();
}
auto result = std::make_unique<D3D11DrawVertexShader>();
VERIFY_OK(device()->CreateInputLayout(layoutDesc,
vertexAttribCount,
blob->GetBufferPointer(),
blob->GetBufferSize(),
&result->layout));
VERIFY_OK(device()->CreateVertexShader(blob->GetBufferPointer(),
blob->GetBufferSize(),
nullptr,
&result->shader));
return result;
}
std::unique_ptr<D3D11DrawPixelShader> D3D11PipelineManager ::
compilePixelShaderBlobToFinalType(ComPtr<ID3DBlob> blob)
{
auto result = std::make_unique<D3D11DrawPixelShader>();
VERIFY_OK(device()->CreatePixelShader(blob->GetBufferPointer(),
blob->GetBufferSize(),
nullptr,
&result->shader));
return result;
}
std::unique_ptr<D3D11DrawPipeline> D3D11PipelineManager::linkPipeline(
const PipelineProps& props,
const D3D11DrawVertexShader& vs,
const D3D11DrawPixelShader& ps)
{
// For D3D11 this just puts the vs and ps into a single structure together.
auto pipeline = std::make_unique<D3D11DrawPipeline>();
#ifdef WITH_RIVE_TOOLS
if (props.synthesizedFailureType ==
SynthesizedFailureType::pipelineCreation ||
props.synthesizedFailureType ==
SynthesizedFailureType::shaderCompilation)
{
// An empty result is what counts as "failed"
return pipeline;
}
#else
std::ignore = props;
#endif
pipeline->m_vertexShader = vs;
pipeline->m_pixelShader = ps;
return pipeline;
}
static D3D11_FILTER filter_for_sampler_filter_options(ImageFilter option)
{
switch (option)
{
case ImageFilter::nearest:
return D3D11_FILTER::D3D11_FILTER_MIN_MAG_MIP_POINT;
case ImageFilter::bilinear:
return D3D11_FILTER::D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
}
RIVE_UNREACHABLE();
return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
}
static D3D11_TEXTURE_ADDRESS_MODE address_mode_for_sampler_filter_options(
ImageWrap option)
{
switch (option)
{
case ImageWrap::clamp:
return D3D11_TEXTURE_ADDRESS_CLAMP;
case ImageWrap::repeat:
return D3D11_TEXTURE_ADDRESS_WRAP;
case ImageWrap::mirror:
return D3D11_TEXTURE_ADDRESS_MIRROR;
}
RIVE_UNREACHABLE();
return D3D11_TEXTURE_ADDRESS_CLAMP;
}
std::unique_ptr<RenderContext> RenderContextD3DImpl::MakeContext(
ComPtr<ID3D11Device> gpu,
ComPtr<ID3D11DeviceContext> gpuContext,
const D3DContextOptions& contextOptions)
{
D3DCapabilities d3dCapabilities;
D3D11_FEATURE_DATA_D3D11_OPTIONS2 d3d11Options2;
if (gpu->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_1)
{
if (SUCCEEDED(gpu->CheckFeatureSupport(
D3D11_FEATURE_D3D11_OPTIONS2,
&d3d11Options2,
sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS2))))
{
d3dCapabilities.supportsRasterizerOrderedViews =
d3d11Options2.ROVsSupported;
if (d3d11Options2.TypedUAVLoadAdditionalFormats)
{
// TypedUAVLoadAdditionalFormats is true. Now check if we can
// both load and store all formats used by Rive (currently only
// RGBA8 and BGRA8):
// https://learn.microsoft.com/en-us/windows/win32/direct3d11/typed-unordered-access-view-loads.
auto check_typed_uav_load = [gpu](DXGI_FORMAT format) {
D3D11_FEATURE_DATA_FORMAT_SUPPORT2 d3d11Format2{};
d3d11Format2.InFormat = format;
if (SUCCEEDED(gpu->CheckFeatureSupport(
D3D11_FEATURE_FORMAT_SUPPORT2,
&d3d11Format2,
sizeof(d3d11Format2))))
{
constexpr UINT loadStoreFlags =
D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD |
D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE;
return (d3d11Format2.OutFormatSupport2 &
loadStoreFlags) == loadStoreFlags;
}
return false;
};
d3dCapabilities.supportsTypedUAVLoadStore =
check_typed_uav_load(DXGI_FORMAT_R8G8B8A8_UNORM) &&
check_typed_uav_load(DXGI_FORMAT_B8G8R8A8_UNORM);
}
}
// Check if we can use HLSL minimum precision types (e.g. min16int)
D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT
d3d11MinPrecisionSupport;
if (SUCCEEDED(gpu->CheckFeatureSupport(
D3D11_FEATURE_SHADER_MIN_PRECISION_SUPPORT,
&d3d11MinPrecisionSupport,
sizeof(d3d11MinPrecisionSupport))))
{
const UINT allStageMinPrecision =
(d3d11MinPrecisionSupport.AllOtherShaderStagesMinPrecision &
d3d11MinPrecisionSupport.PixelShaderMinPrecision);
d3dCapabilities.supportsMin16Precision =
(allStageMinPrecision & D3D11_SHADER_MIN_PRECISION_16_BIT) != 0;
}
}
if (contextOptions.disableRasterizerOrderedViews)
{
d3dCapabilities.supportsRasterizerOrderedViews = false;
}
if (contextOptions.disableTypedUAVLoadStore)
{
d3dCapabilities.supportsTypedUAVLoadStore = false;
}
d3dCapabilities.isIntel = contextOptions.isIntel;
d3dCapabilities.allowsUAVSlot0WithColorOutput = false;
auto renderContextImpl = std::unique_ptr<RenderContextD3DImpl>(
new RenderContextD3DImpl(std::move(gpu),
std::move(gpuContext),
d3dCapabilities,
contextOptions));
return std::make_unique<RenderContext>(std::move(renderContextImpl));
}
RenderContextD3DImpl::RenderContextD3DImpl(
ComPtr<ID3D11Device> gpu,
ComPtr<ID3D11DeviceContext> gpuContext,
const D3DCapabilities& d3dCapabilities,
const D3DContextOptions& d3dContextOptions) :
m_pipelineManager(gpuContext,
gpu,
d3dCapabilities,
d3dContextOptions.shaderCompilationMode),
m_d3dCapabilities(d3dCapabilities),
m_gpu(std::move(gpu)),
m_gpuContext(std::move(gpuContext))
{
m_platformFeatures.clipSpaceBottomUp = true;
m_platformFeatures.framebufferBottomUp = false;
m_platformFeatures.supportsRasterOrdering =
d3dCapabilities.supportsRasterizerOrderedViews;
m_platformFeatures.supportsFragmentShaderAtomics = true;
m_platformFeatures.maxTextureSize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
// Create a default raster state for path and offscreen draws.
D3D11_RASTERIZER_DESC rasterDesc;
rasterDesc.FillMode = D3D11_FILL_SOLID;
rasterDesc.CullMode = D3D11_CULL_BACK;
rasterDesc.FrontCounterClockwise =
FALSE; // FrontCounterClockwise must be FALSE in order to
// match the winding sense of interior triangulations.
rasterDesc.DepthBias = 0;
rasterDesc.SlopeScaledDepthBias = 0;
rasterDesc.DepthBiasClamp = 0;
rasterDesc.DepthClipEnable =
TRUE; // This is the default state which reset to before flushing.
// Details on default state here:
// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_rasterizer_desc
rasterDesc.ScissorEnable = FALSE;
rasterDesc.MultisampleEnable = FALSE;
rasterDesc.AntialiasedLineEnable = FALSE;
VERIFY_OK(m_gpu->CreateRasterizerState(
&rasterDesc,
m_backCulledRasterState[0].ReleaseAndGetAddressOf()));
// ...And with scissor for the atlas.
rasterDesc.ScissorEnable = TRUE;
VERIFY_OK(m_gpu->CreateRasterizerState(
&rasterDesc,
m_atlasRasterState.ReleaseAndGetAddressOf()));
// ...And with wireframe for debugging.
rasterDesc.ScissorEnable = FALSE;
rasterDesc.FillMode = D3D11_FILL_WIREFRAME;
VERIFY_OK(m_gpu->CreateRasterizerState(
&rasterDesc,
m_backCulledRasterState[1].ReleaseAndGetAddressOf()));
// Create a raster state without face culling for drawing image meshes.
rasterDesc.FillMode = D3D11_FILL_SOLID;
rasterDesc.CullMode = D3D11_CULL_NONE;
VERIFY_OK(m_gpu->CreateRasterizerState(
&rasterDesc,
m_doubleSidedRasterState[0].ReleaseAndGetAddressOf()));
// ...And once more with wireframe for debugging.
rasterDesc.FillMode = D3D11_FILL_WIREFRAME;
VERIFY_OK(m_gpu->CreateRasterizerState(
&rasterDesc,
m_doubleSidedRasterState[1].ReleaseAndGetAddressOf()));
// Create the feather texture.
D3D11_TEXTURE1D_DESC featherTextureDesc{};
featherTextureDesc.Format = DXGI_FORMAT_R16_FLOAT;
featherTextureDesc.Width = gpu::GAUSSIAN_TABLE_SIZE;
featherTextureDesc.MipLevels = 1;
featherTextureDesc.ArraySize = FEATHER_TEXTURE_1D_ARRAY_LENGTH;
featherTextureDesc.Usage = D3D11_USAGE_DEFAULT;
featherTextureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
featherTextureDesc.CPUAccessFlags = 0;
featherTextureDesc.MiscFlags = 0;
VERIFY_OK(
m_gpu->CreateTexture1D(&featherTextureDesc,
NULL,
m_featherTexture.ReleaseAndGetAddressOf()));
D3D11_BOX box;
box.left = 0;
box.right = gpu::GAUSSIAN_TABLE_SIZE;
box.top = 0;
box.bottom = 1;
box.front = 0;
box.back = 1;
m_gpuContext->UpdateSubresource(m_featherTexture.Get(),
FEATHER_FUNCTION_ARRAY_INDEX,
&box,
gpu::g_gaussianIntegralTableF16,
sizeof(gpu::g_gaussianIntegralTableF16),
sizeof(gpu::g_gaussianIntegralTableF16));
m_gpuContext->UpdateSubresource(m_featherTexture.Get(),
FEATHER_INVERSE_FUNCTION_ARRAY_INDEX,
&box,
gpu::g_inverseGaussianIntegralTableF16,
sizeof(gpu::g_gaussianIntegralTableF16),
0);
VERIFY_OK(m_gpu->CreateShaderResourceView(
m_featherTexture.Get(),
NULL,
m_featherTextureSRV.ReleaseAndGetAddressOf()));
// Compile the tessellation shaders.
{
m_tessSpanIndexBuffer =
makeSimpleImmutableBuffer(sizeof(gpu::kTessSpanIndices),
D3D11_BIND_INDEX_BUFFER,
gpu::kTessSpanIndices);
}
// Set up the path patch rendering buffers.
PatchVertex patchVertices[kPatchVertexBufferCount];
uint16_t patchIndices[kPatchIndexBufferCount];
GeneratePatchBufferData(patchVertices, patchIndices);
m_patchVertexBuffer = makeSimpleImmutableBuffer(sizeof(patchVertices),
D3D11_BIND_VERTEX_BUFFER,
patchVertices);
m_patchIndexBuffer = makeSimpleImmutableBuffer(sizeof(patchIndices),
D3D11_BIND_INDEX_BUFFER,
patchIndices);
// Set up the imageRect rendering buffers. (gpu::InterlockMode::atomics
// only.)
m_imageRectVertexBuffer =
makeSimpleImmutableBuffer(sizeof(gpu::kImageRectVertices),
D3D11_BIND_VERTEX_BUFFER,
gpu::kImageRectVertices);
m_imageRectIndexBuffer =
makeSimpleImmutableBuffer(sizeof(gpu::kImageRectIndices),
D3D11_BIND_INDEX_BUFFER,
gpu::kImageRectIndices);
// Create buffers for uniforms.
{
D3D11_BUFFER_DESC desc{};
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.ByteWidth = sizeof(gpu::FlushUniforms);
desc.StructureByteStride = sizeof(gpu::FlushUniforms);
VERIFY_OK(
m_gpu->CreateBuffer(&desc,
nullptr,
m_flushUniforms.ReleaseAndGetAddressOf()));
desc.ByteWidth = sizeof(DrawUniforms);
desc.StructureByteStride = sizeof(DrawUniforms);
VERIFY_OK(m_gpu->CreateBuffer(&desc,
nullptr,
m_drawUniforms.ReleaseAndGetAddressOf()));
desc.ByteWidth = sizeof(gpu::ImageDrawUniforms);
desc.StructureByteStride = sizeof(gpu::ImageDrawUniforms);
VERIFY_OK(
m_gpu->CreateBuffer(&desc,
nullptr,
m_imageDrawUniforms.ReleaseAndGetAddressOf()));
}
// Create a linear sampler for the gradient & feather textures.
D3D11_SAMPLER_DESC linearSamplerDesc;
linearSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
linearSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
linearSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
linearSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
linearSamplerDesc.MipLODBias = 0.0f;
linearSamplerDesc.MaxAnisotropy = 1;
linearSamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
linearSamplerDesc.MinLOD = 0;
linearSamplerDesc.MaxLOD = 0;
VERIFY_OK(
m_gpu->CreateSamplerState(&linearSamplerDesc,
m_linearSampler.ReleaseAndGetAddressOf()));
// Create a mipmap sampler for each sampler permutation option.
for (int samplerKey = 0;
samplerKey < ImageSampler::MAX_SAMPLER_PERMUTATIONS;
++samplerKey)
{
auto xWrap = ImageSampler::GetWrapXOptionFromKey(samplerKey);
auto yWrap = ImageSampler::GetWrapYOptionFromKey(samplerKey);
D3D11_SAMPLER_DESC mipmapSamplerDesc;
mipmapSamplerDesc.Filter = filter_for_sampler_filter_options(
ImageSampler::GetFilterOptionFromKey(samplerKey));
mipmapSamplerDesc.AddressU =
address_mode_for_sampler_filter_options(xWrap);
mipmapSamplerDesc.AddressV =
address_mode_for_sampler_filter_options(yWrap);
mipmapSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
mipmapSamplerDesc.MaxAnisotropy = 1;
mipmapSamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
mipmapSamplerDesc.MipLODBias = 0.0f;
mipmapSamplerDesc.MinLOD = 0;
mipmapSamplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
VERIFY_OK(m_gpu->CreateSamplerState(
&mipmapSamplerDesc,
m_samplerStates[samplerKey].ReleaseAndGetAddressOf()));
}
m_gpuContext->VSSetSamplers(FEATHER_TEXTURE_IDX,
1,
m_linearSampler.GetAddressOf());
D3D11_BLEND_DESC srcOverDesc{};
srcOverDesc.RenderTarget[0].BlendEnable = TRUE;
srcOverDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
srcOverDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
srcOverDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
srcOverDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
srcOverDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
srcOverDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
srcOverDesc.RenderTarget[0].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_ALL;
VERIFY_OK(
m_gpu->CreateBlendState(&srcOverDesc,
m_srcOverBlendState.ReleaseAndGetAddressOf()));
D3D11_BLEND_DESC plusDesc{};
plusDesc.RenderTarget[0].BlendEnable = TRUE;
plusDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
plusDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
plusDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
plusDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
plusDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
plusDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
plusDesc.RenderTarget[0].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_ALL;
VERIFY_OK(
m_gpu->CreateBlendState(&plusDesc,
m_plusBlendState.ReleaseAndGetAddressOf()));
D3D11_BLEND_DESC maxDesc = plusDesc;
maxDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_MAX;
maxDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_MAX;
VERIFY_OK(
m_gpu->CreateBlendState(&maxDesc,
m_maxBlendState.ReleaseAndGetAddressOf()));
}
ComPtr<ID3D11Texture2D> RenderContextD3DImpl::makeSimple2DTexture(
DXGI_FORMAT format,
UINT width,
UINT height,
UINT mipLevelCount,
UINT bindFlags,
UINT miscFlags)
{
return make_simple_2d_texture(m_gpu.Get(),
format,
width,
height,
mipLevelCount,
bindFlags,
miscFlags);
}
ComPtr<ID3D11UnorderedAccessView> RenderContextD3DImpl::makeSimple2DUAV(
ID3D11Texture2D* tex,
DXGI_FORMAT format)
{
return make_simple_2d_uav(m_gpu.Get(), tex, format);
}
ComPtr<ID3D11Buffer> RenderContextD3DImpl::makeSimpleImmutableBuffer(
size_t sizeInBytes,
UINT bindFlags,
const void* data)
{
D3D11_BUFFER_DESC desc{};
desc.ByteWidth = math::lossless_numeric_cast<UINT>(sizeInBytes);
desc.Usage = D3D11_USAGE_IMMUTABLE;
desc.BindFlags = bindFlags;
desc.StructureByteStride = sizeof(PatchVertex);
D3D11_SUBRESOURCE_DATA dataDesc{};
dataDesc.pSysMem = data;
ComPtr<ID3D11Buffer> buffer;
VERIFY_OK(
m_gpu->CreateBuffer(&desc, &dataDesc, buffer.ReleaseAndGetAddressOf()));
return buffer;
}
class RenderBufferD3DImpl
: public LITE_RTTI_OVERRIDE(RenderBuffer, RenderBufferD3DImpl)
{
public:
RenderBufferD3DImpl(RenderBufferType renderBufferType,
RenderBufferFlags renderBufferFlags,
size_t sizeInBytes,
ComPtr<ID3D11Device> gpu,
ComPtr<ID3D11DeviceContext> gpuContext) :
lite_rtti_override(renderBufferType, renderBufferFlags, sizeInBytes),
m_gpu(std::move(gpu)),
m_gpuContext(std::move(gpuContext))
{
m_desc.ByteWidth = math::lossless_numeric_cast<UINT>(sizeInBytes);
m_desc.BindFlags = type() == RenderBufferType::vertex
? D3D11_BIND_VERTEX_BUFFER
: D3D11_BIND_INDEX_BUFFER;
if (flags() & RenderBufferFlags::mappedOnceAtInitialization)
{
m_desc.Usage = D3D11_USAGE_IMMUTABLE;
m_desc.CPUAccessFlags = 0;
m_mappedMemoryForImmutableBuffer.reset(new char[sizeInBytes]);
}
else
{
m_desc.Usage = D3D11_USAGE_DYNAMIC;
m_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
VERIFY_OK(m_gpu->CreateBuffer(&m_desc,
nullptr,
m_buffer.ReleaseAndGetAddressOf()));
}
}
ID3D11Buffer* buffer() const { return m_buffer.Get(); }
protected:
void* onMap() override
{
if (flags() & RenderBufferFlags::mappedOnceAtInitialization)
{
assert(m_mappedMemoryForImmutableBuffer);
return m_mappedMemoryForImmutableBuffer.get();
}
else
{
D3D11_MAPPED_SUBRESOURCE mappedSubresource;
m_gpuContext->Map(m_buffer.Get(),
0,
D3D11_MAP_WRITE_DISCARD,
0,
&mappedSubresource);
return mappedSubresource.pData;
}
}
void onUnmap() override
{
if (flags() & RenderBufferFlags::mappedOnceAtInitialization)
{
assert(!m_buffer);
D3D11_SUBRESOURCE_DATA bufferDataDesc{};
bufferDataDesc.pSysMem = m_mappedMemoryForImmutableBuffer.get();
VERIFY_OK(m_gpu->CreateBuffer(&m_desc,
&bufferDataDesc,
m_buffer.ReleaseAndGetAddressOf()));
m_mappedMemoryForImmutableBuffer
.reset(); // This buffer will only be mapped once.
}
else
{
m_gpuContext->Unmap(m_buffer.Get(), 0);
}
}
private:
const ComPtr<ID3D11Device> m_gpu;
const ComPtr<ID3D11DeviceContext> m_gpuContext;
D3D11_BUFFER_DESC m_desc{};
ComPtr<ID3D11Buffer> m_buffer;
std::unique_ptr<char[]> m_mappedMemoryForImmutableBuffer;
};
rcp<RenderBuffer> RenderContextD3DImpl::makeRenderBuffer(
RenderBufferType type,
RenderBufferFlags flags,
size_t sizeInBytes)
{
return make_rcp<RenderBufferD3DImpl>(type,
flags,
sizeInBytes,
m_gpu,
m_gpuContext);
}
class TextureD3DImpl : public Texture
{
public:
TextureD3DImpl(RenderContextD3DImpl* renderContextImpl,
UINT width,
UINT height,
UINT mipLevelCount,
const uint8_t imageDataRGBAPremul[]) :
Texture(width, height)
{
m_texture = renderContextImpl->makeSimple2DTexture(
DXGI_FORMAT_R8G8B8A8_UNORM,
width,
height,
mipLevelCount,
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
D3D11_RESOURCE_MISC_GENERATE_MIPS);
// Specify the top-level image in the mipmap chain.
D3D11_BOX box;
box.left = 0;
box.right = width;
box.top = 0;
box.bottom = height;
box.front = 0;
box.back = 1;
renderContextImpl->gpuContext()->UpdateSubresource(m_texture.Get(),
0,
&box,
imageDataRGBAPremul,
width * 4,
0);
// Create a view and generate mipmaps.
VERIFY_OK(renderContextImpl->gpu()->CreateShaderResourceView(
m_texture.Get(),
NULL,
m_srv.ReleaseAndGetAddressOf()));
renderContextImpl->gpuContext()->GenerateMips(m_srv.Get());
}
ID3D11ShaderResourceView* srv() const { return m_srv.Get(); }
ID3D11ShaderResourceView* const* srvAddressOf() const
{
return m_srv.GetAddressOf();
}
private:
ComPtr<ID3D11Texture2D> m_texture;
ComPtr<ID3D11ShaderResourceView> m_srv;
};
rcp<Texture> RenderContextD3DImpl::makeImageTexture(
uint32_t width,
uint32_t height,
uint32_t mipLevelCount,
const uint8_t imageDataRGBAPremul[])
{
return make_rcp<TextureD3DImpl>(this,
width,
height,
mipLevelCount,
imageDataRGBAPremul);
}
class BufferRingD3D : public BufferRing
{
public:
BufferRingD3D(RenderContextD3DImpl* renderContextImpl,
size_t capacityInBytes,
UINT bindFlags) :
BufferRingD3D(renderContextImpl, capacityInBytes, bindFlags, 0, 0)
{}
ID3D11Buffer* flush(ID3D11DeviceContext* gpuContext)
{
updateBuffer(gpuContext, 0, m_dirtySizeInBytes);
m_dirtySizeInBytes = 0;
return m_buffer.Get();
}
protected:
BufferRingD3D(RenderContextD3DImpl* renderContextImpl,
size_t capacityInBytes,
UINT bindFlags,
UINT elementSizeInBytes,
UINT miscFlags) :
BufferRing(capacityInBytes)
{
D3D11_BUFFER_DESC desc{};
desc.ByteWidth = math::lossless_numeric_cast<UINT>(capacityInBytes);
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = bindFlags;
desc.CPUAccessFlags = 0;
desc.StructureByteStride = elementSizeInBytes;
desc.MiscFlags = miscFlags;
VERIFY_OK(renderContextImpl->gpu()->CreateBuffer(
&desc,
nullptr,
m_buffer.ReleaseAndGetAddressOf()));
}
void* onMapBuffer(int bufferIdx, size_t mapSizeInBytes) override
{
// Use a CPU-side shadow buffer since D3D11 doesn't have an API to map a
// sub-range.
return shadowBuffer();
}
void onUnmapAndSubmitBuffer(int bufferIdx, size_t mapSizeInBytes) override
{
m_dirtySizeInBytes = math::lossless_numeric_cast<UINT>(mapSizeInBytes);
}
void updateBuffer(ID3D11DeviceContext* gpuContext,
UINT offsetInBytes,
UINT sizeInBytes) const
{
assert(offsetInBytes + sizeInBytes <= m_dirtySizeInBytes);
if (sizeInBytes != 0)
{
D3D11_BOX box;
box.left = 0;
box.right = sizeInBytes;
box.top = 0;
box.bottom = 1;
box.front = 0;
box.back = 1;
gpuContext->UpdateSubresource(m_buffer.Get(),
0,
&box,
shadowBuffer() + offsetInBytes,
0,
0);
}
}
ComPtr<ID3D11Buffer> m_buffer;
UINT m_dirtySizeInBytes = 0;
};
class StructuredBufferRingD3D : public BufferRingD3D
{
public:
StructuredBufferRingD3D(RenderContextD3DImpl* renderContextImpl,
size_t capacityInBytes,
UINT elementSizeInBytes) :
BufferRingD3D(renderContextImpl,
capacityInBytes,
D3D11_BIND_SHADER_RESOURCE,
elementSizeInBytes,
D3D11_RESOURCE_MISC_BUFFER_STRUCTURED)
{
assert(capacityInBytes % elementSizeInBytes == 0);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = DXGI_FORMAT_UNKNOWN;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Buffer.NumElements = math::lossless_numeric_cast<UINT>(
capacityInBytes / elementSizeInBytes);
VERIFY_OK(renderContextImpl->gpu()->CreateShaderResourceView(
m_buffer.Get(),
&srvDesc,
m_srv.ReleaseAndGetAddressOf()));
}
ID3D11ShaderResourceView* flush(ID3D11DeviceContext* gpuContext,
UINT offsetInBytes,
UINT sizeInBytes) const
{
updateBuffer(gpuContext, offsetInBytes, sizeInBytes);
return m_srv.Get();
}
protected:
ComPtr<ID3D11ShaderResourceView> m_srv;
};
std::unique_ptr<BufferRing> RenderContextD3DImpl::makeUniformBufferRing(
size_t capacityInBytes)
{
// In D3D we update uniform data inline with commands, rather than filling a
// buffer up front.
return std::make_unique<HeapBufferRing>(capacityInBytes);
}
std::unique_ptr<BufferRing> RenderContextD3DImpl::makeStorageBufferRing(
size_t capacityInBytes,
gpu::StorageBufferStructure bufferStructure)
{
return capacityInBytes != 0
? std::make_unique<StructuredBufferRingD3D>(
this,
capacityInBytes,
gpu::StorageBufferElementSizeInBytes(bufferStructure))
: nullptr;
}
std::unique_ptr<BufferRing> RenderContextD3DImpl::makeVertexBufferRing(
size_t capacityInBytes)
{
return capacityInBytes != 0
? std::make_unique<BufferRingD3D>(this,
capacityInBytes,
D3D11_BIND_VERTEX_BUFFER)
: nullptr;
}
RenderTargetD3D::RenderTargetD3D(RenderContextD3DImpl* renderContextImpl,
uint32_t width,
uint32_t height) :
RenderTarget(width, height),
m_gpu(renderContextImpl->gpu()),
m_gpuSupportsTypedUAVLoadStore(
renderContextImpl->d3dCapabilities().supportsTypedUAVLoadStore)
{}
void RenderTargetD3D::setTargetTexture(ComPtr<ID3D11Texture2D> tex)
{
if (tex != nullptr)
{
D3D11_TEXTURE2D_DESC desc;
tex->GetDesc(&desc);
#ifdef DEBUG
assert(desc.Width == width());
assert(desc.Height == height());
assert(desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM ||
desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM ||
desc.Format == DXGI_FORMAT_R8G8B8A8_TYPELESS ||
desc.Format == DXGI_FORMAT_B8G8R8A8_TYPELESS);
#endif
m_targetTextureSupportsUAV =
(desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) &&
m_gpuSupportsTypedUAVLoadStore;
m_targetFormat = desc.Format;
}
else
{
m_targetTextureSupportsUAV = false;
}
m_targetTexture = std::move(tex);
m_targetRTV = nullptr;
m_targetUAV = nullptr;
}
ID3D11RenderTargetView* RenderTargetD3D::targetRTV()
{
if (m_targetRTV == nullptr && m_targetTexture != nullptr)
{
D3D11_RENDER_TARGET_VIEW_DESC desc{};
desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
switch (m_targetFormat)
{
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
break;
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
break;
default:
RIVE_UNREACHABLE();
}
VERIFY_OK(m_gpu->CreateRenderTargetView(
m_targetTexture.Get(),
&desc,
m_targetRTV.ReleaseAndGetAddressOf()));
}
return m_targetRTV.Get();
}
ID3D11Texture2D* RenderTargetD3D::offscreenTexture()
{
assert(!m_targetTextureSupportsUAV);
if (m_offscreenTexture == nullptr)
{
m_offscreenTexture =
make_simple_2d_texture(m_gpu.Get(),
DXGI_FORMAT_R8G8B8A8_TYPELESS,
width(),
height(),
1,
D3D11_BIND_UNORDERED_ACCESS);
}
return m_offscreenTexture.Get();
}
ID3D11UnorderedAccessView* RenderTargetD3D::targetUAV()
{
if (m_targetUAV == nullptr)
{
if (auto* uavTexture = m_targetTextureSupportsUAV
? m_targetTexture.Get()
: offscreenTexture())
{
DXGI_FORMAT targetUavFormat;
switch (m_targetFormat)
{
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
targetUavFormat = m_gpuSupportsTypedUAVLoadStore
? DXGI_FORMAT_R8G8B8A8_UNORM
: DXGI_FORMAT_R32_UINT;
break;
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
targetUavFormat = m_gpuSupportsTypedUAVLoadStore
? DXGI_FORMAT_B8G8R8A8_UNORM
: DXGI_FORMAT_R32_UINT;
break;
default:
RIVE_UNREACHABLE();
}
m_targetUAV =
make_simple_2d_uav(m_gpu.Get(), uavTexture, targetUavFormat);
}
}
return m_targetUAV.Get();
}
ID3D11UnorderedAccessView* RenderTargetD3D::clipUAV()
{
if (m_clipTexture == nullptr)
{
m_clipTexture = make_simple_2d_texture(m_gpu.Get(),
DXGI_FORMAT_R32_UINT,
width(),
height(),
1,
D3D11_BIND_UNORDERED_ACCESS);
}
if (m_clipUAV == nullptr)
{
m_clipUAV = make_simple_2d_uav(m_gpu.Get(),
m_clipTexture.Get(),
DXGI_FORMAT_R32_UINT);
}
return m_clipUAV.Get();
}
ID3D11UnorderedAccessView* RenderTargetD3D::scratchColorUAV()
{
if (m_scratchColorTexture == nullptr)
{
m_scratchColorTexture =
make_simple_2d_texture(m_gpu.Get(),
DXGI_FORMAT_R8G8B8A8_TYPELESS,
width(),
height(),
1,
D3D11_BIND_UNORDERED_ACCESS);
}
if (m_scratchColorUAV == nullptr)
{
m_scratchColorUAV = make_simple_2d_uav(m_gpu.Get(),
m_scratchColorTexture.Get(),
m_gpuSupportsTypedUAVLoadStore
? DXGI_FORMAT_R8G8B8A8_UNORM
: DXGI_FORMAT_R32_UINT);
}
return m_scratchColorUAV.Get();
}
ID3D11UnorderedAccessView* RenderTargetD3D::coverageUAV()
{
if (m_coverageTexture == nullptr)
{
m_coverageTexture = make_simple_2d_texture(m_gpu.Get(),
DXGI_FORMAT_R32_UINT,
width(),
height(),
1,
D3D11_BIND_UNORDERED_ACCESS);
}
if (m_coverageUAV == nullptr)
{
m_coverageUAV = make_simple_2d_uav(m_gpu.Get(),
m_coverageTexture.Get(),
DXGI_FORMAT_R32_UINT);
}
return m_coverageUAV.Get();
}
void RenderContextD3DImpl::resizeGradientTexture(uint32_t width,
uint32_t height)
{
if (width == 0 || height == 0)
{
m_gradTexture = nullptr;
m_gradTextureSRV = nullptr;
m_gradTextureRTV = nullptr;
}
else
{
m_gradTexture = makeSimple2DTexture(DXGI_FORMAT_R8G8B8A8_UNORM,
width,
height,
1,
D3D11_BIND_RENDER_TARGET |
D3D11_BIND_SHADER_RESOURCE);
VERIFY_OK(m_gpu->CreateShaderResourceView(
m_gradTexture.Get(),
NULL,
m_gradTextureSRV.ReleaseAndGetAddressOf()));
VERIFY_OK(m_gpu->CreateRenderTargetView(
m_gradTexture.Get(),
NULL,
m_gradTextureRTV.ReleaseAndGetAddressOf()));
}
}
void RenderContextD3DImpl::resizeTessellationTexture(uint32_t width,
uint32_t height)
{
if (width == 0 || height == 0)
{
m_tessTexture = nullptr;
m_tessTextureSRV = nullptr;
m_tessTextureRTV = nullptr;
}
else
{
m_tessTexture = makeSimple2DTexture(DXGI_FORMAT_R32G32B32A32_UINT,
width,
height,
1,
D3D11_BIND_RENDER_TARGET |
D3D11_BIND_SHADER_RESOURCE);
VERIFY_OK(m_gpu->CreateShaderResourceView(
m_tessTexture.Get(),
NULL,
m_tessTextureSRV.ReleaseAndGetAddressOf()));
VERIFY_OK(m_gpu->CreateRenderTargetView(
m_tessTexture.Get(),
NULL,
m_tessTextureRTV.ReleaseAndGetAddressOf()));
}
}
void RenderContextD3DImpl::resizeAtlasTexture(uint32_t width, uint32_t height)
{
if (width == 0 || height == 0)
{
m_atlasTexture = nullptr;
m_atlasTextureSRV = nullptr;
m_atlasTextureRTV = nullptr;
}
else
{
m_atlasTexture = makeSimple2DTexture(DXGI_FORMAT_R32_FLOAT,
width,
height,
1,
D3D11_BIND_RENDER_TARGET |
D3D11_BIND_SHADER_RESOURCE);
VERIFY_OK(m_gpu->CreateShaderResourceView(
m_atlasTexture.Get(),
NULL,
m_atlasTextureSRV.ReleaseAndGetAddressOf()));
VERIFY_OK(m_gpu->CreateRenderTargetView(
m_atlasTexture.Get(),
NULL,
m_atlasTextureRTV.ReleaseAndGetAddressOf()));
}
}
static const char* heap_buffer_contents(const BufferRing* bufferRing)
{
assert(bufferRing != nullptr);
auto heapBuffer = static_cast<const HeapBufferRing*>(bufferRing);
return reinterpret_cast<const char*>(heapBuffer->contents());
}
static ID3D11Buffer* flush_buffer(ID3D11DeviceContext* gpuContext,
BufferRing* bufferRing)
{
assert(bufferRing != nullptr);
return static_cast<BufferRingD3D*>(bufferRing)->flush(gpuContext);
}
template <typename HighLevelStruct>
ID3D11ShaderResourceView* flush_structured_buffer(
ID3D11DeviceContext* gpuContext,
BufferRing* bufferRing,
UINT highLevelStructCount,
UINT firstHighLevelStruct)
{
return static_cast<const StructuredBufferRingD3D*>(bufferRing)
->flush(gpuContext,
firstHighLevelStruct * sizeof(HighLevelStruct),
highLevelStructCount * sizeof(HighLevelStruct));
}
static void blit_sub_rect(ID3D11DeviceContext* gpuContext,
ID3D11Texture2D* dst,
ID3D11Texture2D* src,
const IAABB& rect)
{
D3D11_BOX updateBox = {
static_cast<UINT>(rect.left),
static_cast<UINT>(rect.top),
0,
static_cast<UINT>(rect.right),
static_cast<UINT>(rect.bottom),
1,
};
gpuContext->CopySubresourceRegion(dst,
0,
updateBox.left,
updateBox.top,
0,
src,
0,
&updateBox);
}
static D3D11_RECT make_scissor(const TAABB<uint16_t> scissor)
{
D3D11_RECT rect;
rect.left = scissor.left;
rect.top = scissor.top;
rect.right = scissor.right;
rect.bottom = scissor.bottom;
return rect;
}
void RenderContextD3DImpl::flush(const FlushDescriptor& desc)
{
assert(desc.interlockMode != gpu::InterlockMode::clockwiseAtomic);
auto renderTarget = static_cast<RenderTargetD3D*>(desc.renderTarget);
m_gpuContext->ClearState();
// All programs use the same set of per-flush uniforms.
m_gpuContext->UpdateSubresource(
m_flushUniforms.Get(),
0,
NULL,
heap_buffer_contents(flushUniformBufferRing()) +
desc.flushUniformDataOffsetInBytes,
0,
0);
ID3D11Buffer* uniformBuffers[] = {m_flushUniforms.Get(),
m_drawUniforms.Get(),
m_imageDrawUniforms.Get()};
static_assert(PATH_BASE_INSTANCE_UNIFORM_BUFFER_IDX ==
FLUSH_UNIFORM_BUFFER_IDX + 1);
static_assert(IMAGE_DRAW_UNIFORM_BUFFER_IDX ==
PATH_BASE_INSTANCE_UNIFORM_BUFFER_IDX + 1);
m_gpuContext->VSSetConstantBuffers(FLUSH_UNIFORM_BUFFER_IDX,
std::size(uniformBuffers),
uniformBuffers);
m_gpuContext->PSSetConstantBuffers(FLUSH_UNIFORM_BUFFER_IDX,
std::size(uniformBuffers),
uniformBuffers);
// All programs use the same storage buffers.
ID3D11ShaderResourceView* storageBufferBufferSRVs[] = {
desc.pathCount > 0
? flush_structured_buffer<gpu::PathData>(
m_gpuContext.Get(),
pathBufferRing(),
desc.pathCount,
math::lossless_numeric_cast<UINT>(desc.firstPath))
: nullptr,
desc.pathCount > 0
? flush_structured_buffer<gpu::PaintData>(
m_gpuContext.Get(),
paintBufferRing(),
desc.pathCount,
math::lossless_numeric_cast<UINT>(desc.firstPaint))
: nullptr,
desc.pathCount > 0
? flush_structured_buffer<gpu::PaintAuxData>(
m_gpuContext.Get(),
paintAuxBufferRing(),
desc.pathCount,
math::lossless_numeric_cast<UINT>(desc.firstPaintAux))
: nullptr,
desc.contourCount > 0
? flush_structured_buffer<gpu::ContourData>(
m_gpuContext.Get(),
contourBufferRing(),
desc.contourCount,
math::lossless_numeric_cast<UINT>(desc.firstContour))
: nullptr,
};
static_assert(PAINT_BUFFER_IDX == PATH_BUFFER_IDX + 1);
static_assert(PAINT_AUX_BUFFER_IDX == PAINT_BUFFER_IDX + 1);
static_assert(CONTOUR_BUFFER_IDX == PAINT_AUX_BUFFER_IDX + 1);
m_gpuContext->VSSetShaderResources(PATH_BUFFER_IDX,
std::size(storageBufferBufferSRVs),
storageBufferBufferSRVs);
if (desc.interlockMode == gpu::InterlockMode::atomics)
{
// Atomic mode accesses the paint buffers from the pixel shader.
m_gpuContext->PSSetShaderResources(PAINT_BUFFER_IDX,
2,
storageBufferBufferSRVs + 1);
}
// All programs use the same feather texture.
m_gpuContext->VSSetShaderResources(FEATHER_TEXTURE_IDX,
1,
m_featherTextureSRV.GetAddressOf());
m_gpuContext->PSSetShaderResources(FEATHER_TEXTURE_IDX,
1,
m_featherTextureSRV.GetAddressOf());
// All programs use the same samplers.
ID3D11SamplerState* samplers[4] = {
m_linearSampler.Get(),
m_linearSampler.Get(),
m_linearSampler.Get(),
// 0 is the default which is MIN_MAG_MIP_LINEAR
m_samplerStates[ImageSampler::LINEAR_CLAMP_SAMPLER_KEY].Get(),
};
static_assert(FEATHER_TEXTURE_IDX == GRAD_TEXTURE_IDX + 1);
static_assert(ATLAS_TEXTURE_IDX == FEATHER_TEXTURE_IDX + 1);
static_assert(IMAGE_TEXTURE_IDX == ATLAS_TEXTURE_IDX + 1);
m_gpuContext->PSSetSamplers(GRAD_TEXTURE_IDX, 4, samplers);
m_gpuContext->VSSetSamplers(GRAD_TEXTURE_IDX, 4, samplers);
// Render the complex color ramps to the gradient texture.
if (desc.gradSpanCount > 0)
{
ID3D11Buffer* gradSpanBuffer =
flush_buffer(m_gpuContext.Get(), gradSpanBufferRing());
UINT gradStride = sizeof(GradientSpan);
UINT gradOffset = 0;
m_gpuContext->IASetVertexBuffers(0,
1,
&gradSpanBuffer,
&gradStride,
&gradOffset);
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
m_pipelineManager.setColorRampState();
D3D11_VIEWPORT viewport = {0,
0,
static_cast<float>(kGradTextureWidth),
static_cast<float>(desc.gradDataHeight),
0,
1};
m_gpuContext->RSSetViewports(1, &viewport);
// Unbind the gradient texture before rendering it.
ID3D11ShaderResourceView* nullTextureView = nullptr;
m_gpuContext->PSSetShaderResources(GRAD_TEXTURE_IDX,
1,
&nullTextureView);
m_gpuContext->OMSetRenderTargets(1,
m_gradTextureRTV.GetAddressOf(),
NULL);
m_gpuContext->DrawInstanced(
gpu::GRAD_SPAN_TRI_STRIP_VERTEX_COUNT,
desc.gradSpanCount,
0,
math::lossless_numeric_cast<UINT>(desc.firstGradSpan));
}
// Tessellate all curves into vertices in the tessellation texture.
if (desc.tessVertexSpanCount > 0)
{
ID3D11Buffer* tessSpanBuffer =
flush_buffer(m_gpuContext.Get(), tessSpanBufferRing());
UINT tessStride = sizeof(TessVertexSpan);
UINT tessOffset = 0;
m_gpuContext->IASetVertexBuffers(0,
1,
&tessSpanBuffer,
&tessStride,
&tessOffset);
m_gpuContext->IASetIndexBuffer(m_tessSpanIndexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0);
m_pipelineManager.setTesselationState();
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Unbind the tessellation texture before rendering it.
ID3D11ShaderResourceView* nullTessTextureView = NULL;
m_gpuContext->VSSetShaderResources(TESS_VERTEX_TEXTURE_IDX,
1,
&nullTessTextureView);
D3D11_VIEWPORT viewport = {0,
0,
static_cast<float>(kTessTextureWidth),
static_cast<float>(desc.tessDataHeight),
0,
1};
m_gpuContext->RSSetViewports(1, &viewport);
m_gpuContext->OMSetRenderTargets(1,
m_tessTextureRTV.GetAddressOf(),
NULL);
m_gpuContext->DrawIndexedInstanced(
std::size(gpu::kTessSpanIndices),
desc.tessVertexSpanCount,
0,
0,
math::lossless_numeric_cast<UINT>(desc.firstTessVertexSpan));
if (m_d3dCapabilities.isIntel)
{
// FIXME! Intel needs this flush! Driver bug? Find a lighter
// workaround?
m_gpuContext->Flush();
}
}
ID3D11Buffer* vertexBuffers[3] = {
m_patchVertexBuffer.Get(),
desc.hasTriangleVertices
? flush_buffer(m_gpuContext.Get(), triangleBufferRing())
: NULL,
m_imageRectVertexBuffer.Get()};
UINT vertexStrides[3] = {sizeof(gpu::PatchVertex),
sizeof(gpu::TriangleVertex),
sizeof(gpu::ImageRectVertex)};
UINT vertexOffsets[3] = {0, 0, 0};
static_assert(PATCH_VERTEX_DATA_SLOT == 0);
static_assert(TRIANGLE_VERTEX_DATA_SLOT == 1);
static_assert(IMAGE_RECT_VERTEX_DATA_SLOT == 2);
m_gpuContext->IASetVertexBuffers(0,
3,
vertexBuffers,
vertexStrides,
vertexOffsets);
// Unbind the tess and grad texture as a render target before binding them
// as images.
m_gpuContext->OMSetRenderTargets(0, NULL, NULL);
m_gpuContext->VSSetShaderResources(TESS_VERTEX_TEXTURE_IDX,
1,
m_tessTextureSRV.GetAddressOf());
m_gpuContext->PSSetShaderResources(GRAD_TEXTURE_IDX,
1,
m_gradTextureSRV.GetAddressOf());
// Render the atlas if we have any offscreen feathers.
if ((desc.atlasFillBatchCount | desc.atlasStrokeBatchCount) != 0)
{
float clearZero[4]{};
m_gpuContext->ClearRenderTargetView(m_atlasTextureRTV.Get(), clearZero);
m_pipelineManager.setAtlasVertexState();
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_gpuContext->IASetIndexBuffer(m_patchIndexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0);
m_gpuContext->RSSetState(m_atlasRasterState.Get());
D3D11_VIEWPORT viewport = {0,
0,
static_cast<float>(desc.atlasContentWidth),
static_cast<float>(desc.atlasContentHeight),
0,
1};
m_gpuContext->RSSetViewports(1, &viewport);
m_gpuContext->OMSetRenderTargets(1,
m_atlasTextureRTV.GetAddressOf(),
NULL);
if (desc.atlasFillBatchCount != 0)
{
m_pipelineManager.setAtlasFillState();
m_gpuContext->OMSetBlendState(m_plusBlendState.Get(),
NULL,
0xffffffff);
for (size_t i = 0; i < desc.atlasFillBatchCount; ++i)
{
const gpu::AtlasDrawBatch& fillBatch = desc.atlasFillBatches[i];
D3D11_RECT scissor = make_scissor(fillBatch.scissor);
m_gpuContext->RSSetScissorRects(1, &scissor);
DrawUniforms drawUniforms(fillBatch.basePatch);
m_gpuContext->UpdateSubresource(m_drawUniforms.Get(),
0,
NULL,
&drawUniforms,
0,
0);
m_gpuContext->DrawIndexedInstanced(
gpu::kMidpointFanCenterAAPatchIndexCount,
fillBatch.patchCount,
gpu::kMidpointFanCenterAAPatchBaseIndex,
0,
fillBatch.basePatch);
}
}
if (desc.atlasStrokeBatchCount != 0)
{
m_pipelineManager.setAtlasStrokeState();
m_gpuContext->OMSetBlendState(m_maxBlendState.Get(),
NULL,
0xffffffff);
for (size_t i = 0; i < desc.atlasStrokeBatchCount; ++i)
{
const gpu::AtlasDrawBatch& strokeBatch =
desc.atlasStrokeBatches[i];
D3D11_RECT scissor = make_scissor(strokeBatch.scissor);
m_gpuContext->RSSetScissorRects(1, &scissor);
DrawUniforms drawUniforms(strokeBatch.basePatch);
m_gpuContext->UpdateSubresource(m_drawUniforms.Get(),
0,
NULL,
&drawUniforms,
0,
0);
m_gpuContext->DrawIndexedInstanced(
gpu::kMidpointFanPatchBorderIndexCount,
strokeBatch.patchCount,
gpu::kMidpointFanPatchBaseIndex,
0,
strokeBatch.basePatch);
}
}
m_gpuContext->OMSetBlendState(NULL, NULL, 0xffffffff);
}
// Setup and clear the PLS textures.
switch (desc.colorLoadAction)
{
case gpu::LoadAction::clear:
if (desc.atomicFixedFunctionColorOutput)
{
float clearColor4f[4];
UnpackColorToRGBA32FPremul(desc.colorClearValue, clearColor4f);
m_gpuContext->ClearRenderTargetView(renderTarget->targetRTV(),
clearColor4f);
}
else if (m_d3dCapabilities.supportsTypedUAVLoadStore)
{
float clearColor4f[4];
UnpackColorToRGBA32FPremul(desc.colorClearValue, clearColor4f);
m_gpuContext->ClearUnorderedAccessViewFloat(
renderTarget->targetUAV(),
clearColor4f);
}
else
{
UINT clearColorui[4] = {
gpu::SwizzleRiveColorToRGBAPremul(desc.colorClearValue)};
m_gpuContext->ClearUnorderedAccessViewUint(
renderTarget->targetUAV(),
clearColorui);
}
break;
case gpu::LoadAction::preserveRenderTarget:
if (!desc.atomicFixedFunctionColorOutput &&
!renderTarget->targetTextureSupportsUAV())
{
// We're rendering to an offscreen UAV and preserving the
// target. Copy the target texture over.
blit_sub_rect(m_gpuContext.Get(),
renderTarget->offscreenTexture(),
renderTarget->targetTexture(),
desc.renderTargetUpdateBounds);
}
break;
case gpu::LoadAction::dontCare:
break;
}
if (desc.combinedShaderFeatures & gpu::ShaderFeatures::ENABLE_CLIPPING)
{
constexpr static UINT kZero[4]{};
m_gpuContext->ClearUnorderedAccessViewUint(renderTarget->clipUAV(),
kZero);
}
{
UINT coverageClear[4]{desc.coverageClearValue};
m_gpuContext->ClearUnorderedAccessViewUint(renderTarget->coverageUAV(),
coverageClear);
}
// Execute the DrawList.
ID3D11RenderTargetView* targetRTV =
desc.atomicFixedFunctionColorOutput ? renderTarget->targetRTV() : NULL;
ID3D11UnorderedAccessView* plsUAVs[] = {
desc.atomicFixedFunctionColorOutput ? NULL : renderTarget->targetUAV(),
renderTarget->clipUAV(),
desc.interlockMode == gpu::InterlockMode::rasterOrdering
? renderTarget->scratchColorUAV()
: NULL, // Atomic mode doesn't use the scratchColor.
renderTarget->coverageUAV(),
};
static_assert(COLOR_PLANE_IDX == 0);
static_assert(CLIP_PLANE_IDX == 1);
static_assert(SCRATCH_COLOR_PLANE_IDX == 2);
static_assert(COVERAGE_PLANE_IDX == 3);
m_gpuContext->OMSetRenderTargetsAndUnorderedAccessViews(
desc.atomicFixedFunctionColorOutput ? 1 : 0,
&targetRTV,
NULL,
desc.atomicFixedFunctionColorOutput ? 1 : 0,
desc.atomicFixedFunctionColorOutput ? std::size(plsUAVs) - 1
: std::size(plsUAVs),
desc.atomicFixedFunctionColorOutput ? plsUAVs + 1 : plsUAVs,
NULL);
if (desc.atomicFixedFunctionColorOutput)
{
// When rendering directly to the target RTV, we use the built-in blend
// hardware for opacity and antialiasing.
m_gpuContext->OMSetBlendState(m_srcOverBlendState.Get(),
NULL,
0xffffffff);
}
D3D11_VIEWPORT viewport = {0,
0,
static_cast<float>(renderTarget->width()),
static_cast<float>(renderTarget->height()),
0,
1};
m_gpuContext->RSSetViewports(1, &viewport);
// Set this last, when the atlas texture is no longer bound as a render
// target.
m_gpuContext->PSSetShaderResources(ATLAS_TEXTURE_IDX,
1,
m_atlasTextureSRV.GetAddressOf());
m_gpuContext->PSSetConstantBuffers(IMAGE_DRAW_UNIFORM_BUFFER_IDX,
1,
m_imageDrawUniforms.GetAddressOf());
const char* const imageDrawUniformData =
heap_buffer_contents(imageDrawUniformBufferRing());
bool renderPassHasCoalescedResolveAndTransfer =
desc.interlockMode == gpu::InterlockMode::atomics &&
!desc.atomicFixedFunctionColorOutput &&
!renderTarget->targetTextureSupportsUAV();
for (const DrawBatch& batch : *desc.drawList)
{
DrawType drawType = batch.drawType;
auto shaderFeatures = desc.interlockMode == gpu::InterlockMode::atomics
? desc.combinedShaderFeatures
: batch.shaderFeatures;
auto shaderMiscFlags = batch.shaderMiscFlags;
if (drawType == gpu::DrawType::renderPassResolve &&
renderPassHasCoalescedResolveAndTransfer)
{
assert(desc.interlockMode == gpu::InterlockMode::atomics);
shaderMiscFlags |=
gpu::ShaderMiscFlags::coalescedResolveAndTransfer;
}
if (desc.atomicFixedFunctionColorOutput)
{
shaderMiscFlags |= gpu::ShaderMiscFlags::fixedFunctionColorOutput;
}
if (desc.interlockMode == gpu::InterlockMode::rasterOrdering &&
(batch.drawContents & gpu::DrawContents::clockwiseFill))
{
shaderMiscFlags |= gpu::ShaderMiscFlags::clockwiseFill;
}
if (!m_pipelineManager.setPipelineState(drawType,
shaderFeatures,
desc.interlockMode,
shaderMiscFlags,
m_platformFeatures
#ifdef WITH_RIVE_TOOLS
,
desc.synthesizedFailureType
#endif
))
{
// There was an issue getting either the requested pipeline state or
// its ubershader counterpart so we cannot draw anything.
continue;
}
if (auto imageTextureD3D =
static_cast<const TextureD3DImpl*>(batch.imageTexture))
{
m_gpuContext->PSSetShaderResources(IMAGE_TEXTURE_IDX,
1,
imageTextureD3D->srvAddressOf());
}
{
// we should never get a sampler option that is greater then our
// array size
assert(batch.imageSampler.asKey() <
ImageSampler::MAX_SAMPLER_PERMUTATIONS);
ID3D11SamplerState* samplers[1] = {
m_samplerStates[batch.imageSampler.asKey()].Get()};
m_gpuContext->PSSetSamplers(IMAGE_SAMPLER_IDX, 1, samplers);
}
switch (drawType)
{
case DrawType::midpointFanPatches:
case DrawType::midpointFanCenterAAPatches:
case DrawType::outerCurvePatches:
{
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_gpuContext->IASetIndexBuffer(m_patchIndexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0);
m_gpuContext->RSSetState(
m_backCulledRasterState[desc.wireframe].Get());
DrawUniforms drawUniforms(batch.baseElement);
m_gpuContext->UpdateSubresource(m_drawUniforms.Get(),
0,
NULL,
&drawUniforms,
0,
0);
m_gpuContext->DrawIndexedInstanced(PatchIndexCount(drawType),
batch.elementCount,
PatchBaseIndex(drawType),
0,
batch.baseElement);
break;
}
case DrawType::interiorTriangulation:
case DrawType::atlasBlit:
{
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_gpuContext->RSSetState(
m_backCulledRasterState[desc.wireframe].Get());
m_gpuContext->Draw(batch.elementCount, batch.baseElement);
break;
}
case DrawType::imageRect:
{
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_gpuContext->IASetIndexBuffer(m_imageRectIndexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0);
m_gpuContext->RSSetState(
m_doubleSidedRasterState[desc.wireframe].Get());
m_gpuContext->UpdateSubresource(m_imageDrawUniforms.Get(),
0,
NULL,
imageDrawUniformData +
batch.imageDrawDataOffset,
0,
0);
m_gpuContext->DrawIndexed(std::size(gpu::kImageRectIndices),
0,
0);
break;
}
case DrawType::imageMesh:
{
LITE_RTTI_CAST_OR_BREAK(vertexBuffer,
RenderBufferD3DImpl*,
batch.vertexBuffer);
LITE_RTTI_CAST_OR_BREAK(uvBuffer,
RenderBufferD3DImpl*,
batch.uvBuffer);
LITE_RTTI_CAST_OR_BREAK(indexBuffer,
RenderBufferD3DImpl*,
batch.indexBuffer);
ID3D11Buffer* imageMeshBuffers[] = {vertexBuffer->buffer(),
uvBuffer->buffer()};
UINT imageMeshStrides[] = {sizeof(Vec2D), sizeof(Vec2D)};
UINT imageMeshOffsets[] = {0, 0};
m_gpuContext->IASetVertexBuffers(IMAGE_MESH_VERTEX_DATA_SLOT,
2,
imageMeshBuffers,
imageMeshStrides,
imageMeshOffsets);
static_assert(IMAGE_MESH_UV_DATA_SLOT ==
IMAGE_MESH_VERTEX_DATA_SLOT + 1);
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_gpuContext->IASetIndexBuffer(indexBuffer->buffer(),
DXGI_FORMAT_R16_UINT,
0);
m_gpuContext->RSSetState(
m_doubleSidedRasterState[desc.wireframe].Get());
m_gpuContext->UpdateSubresource(m_imageDrawUniforms.Get(),
0,
NULL,
imageDrawUniformData +
batch.imageDrawDataOffset,
0,
0);
m_gpuContext->DrawIndexed(batch.elementCount,
batch.baseElement,
0);
break;
}
case DrawType::renderPassResolve:
assert(desc.interlockMode == gpu::InterlockMode::atomics);
m_gpuContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
m_gpuContext->RSSetState(m_backCulledRasterState[0].Get());
if (renderPassHasCoalescedResolveAndTransfer)
{
// Bind the actual target texture as the render target for
// the PLS resolve, so we don't have to copy to it after the
// render pass. (And ince we're changing the render target,
// this also better be the final batch of the render pass.)
assert(&batch == &desc.drawList->tail());
assert(!desc.atomicFixedFunctionColorOutput);
assert(!renderTarget->targetTextureSupportsUAV());
ID3D11RenderTargetView* resolveRTV =
renderTarget->targetRTV();
ID3D11UnorderedAccessView* resolveUAVs[] = {
renderTarget->clipUAV(),
renderTarget
->targetUAV(), // Bind the target UAV (for reading)
// to a different slot for the
// resolve because D3D doesn't let us
// use slot 0 when there's a render
// target.
renderTarget->coverageUAV(),
};
static_assert(CLIP_PLANE_IDX == 1);
static_assert(COALESCED_OFFSCREEN_COLOR_PLANE_IDX == 2);
static_assert(COVERAGE_PLANE_IDX == 3);
m_gpuContext->OMSetRenderTargetsAndUnorderedAccessViews(
1,
&resolveRTV,
NULL,
1,
std::size(resolveUAVs),
resolveUAVs,
NULL);
}
m_gpuContext->Draw(4, 0);
break;
case DrawType::msaaStrokes:
case DrawType::msaaMidpointFanBorrowedCoverage:
case DrawType::msaaMidpointFans:
case DrawType::msaaMidpointFanStencilReset:
case DrawType::msaaMidpointFanPathsStencil:
case DrawType::msaaMidpointFanPathsCover:
case DrawType::msaaOuterCubics:
case DrawType::msaaStencilClipReset:
case DrawType::renderPassInitialize:
RIVE_UNREACHABLE();
}
}
if (desc.interlockMode == gpu::InterlockMode::rasterOrdering &&
!renderTarget->targetTextureSupportsUAV())
{
// We rendered to an offscreen UAV and did not resolve to the
// renderTarget. Copy back to the main target.
assert(!desc.atomicFixedFunctionColorOutput);
assert(!renderPassHasCoalescedResolveAndTransfer);
blit_sub_rect(m_gpuContext.Get(),
renderTarget->targetTexture(),
renderTarget->offscreenTexture(),
desc.renderTargetUpdateBounds);
}
}
} // namespace rive::gpu