Initial D3D11 backend for PLS Diffs= 9c1d1e4ff Initial D3D11 backend for PLS (#5534) Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head index 99b7c41..0b671ab 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -dcf320c64584505acef32ddf5f316fa5cb6d0813 +9c1d1e4ffbafbe4ab9f649b8228f721ab5e836d0
diff --git a/include/rive/pls/buffer_ring.hpp b/include/rive/pls/buffer_ring.hpp index 01b1b39..149bb49 100644 --- a/include/rive/pls/buffer_ring.hpp +++ b/include/rive/pls/buffer_ring.hpp
@@ -139,7 +139,7 @@ submitTexels(bufferIdx, updateWidthInTexels, updateHeight); } - virtual void submitTexels(int textureIdx, size_t width, size_t height) = 0; + virtual void submitTexels(int textureIdx, size_t updateWidthInTexels, size_t updateHeight) = 0; const Format m_format; const size_t m_widthInItems;
diff --git a/include/rive/pls/d3d/d3d11.hpp b/include/rive/pls/d3d/d3d11.hpp new file mode 100644 index 0000000..762f245 --- /dev/null +++ b/include/rive/pls/d3d/d3d11.hpp
@@ -0,0 +1,21 @@ +/* + * Copyright 2023 Rive + */ + +#pragma once + +#define NOMINMAX +#include <d3d11.h> +#include <wrl/client.h> + +using Microsoft::WRL::ComPtr; + +#define VERIFY_OK(CODE) \ + { \ + HRESULT hr = (CODE); \ + if (hr != S_OK) \ + { \ + fprintf(stderr, __FILE__ ":%u: D3D error 0x%lx: %s\n", __LINE__, hr, #CODE); \ + exit(-1); \ + } \ + }
diff --git a/include/rive/pls/d3d/pls_render_context_d3d.hpp b/include/rive/pls/d3d/pls_render_context_d3d.hpp new file mode 100644 index 0000000..1979daf --- /dev/null +++ b/include/rive/pls/d3d/pls_render_context_d3d.hpp
@@ -0,0 +1,119 @@ +/* + * Copyright 2023 Rive + */ + +#pragma once + +#include "rive/pls/d3d/d3d11.hpp" +#include "rive/pls/pls_render_context.hpp" +#include <map> + +namespace rive::pls +{ +class PLSRenderTargetD3D; + +// D3D backend implementation of PLSRenderTarget. +class PLSRenderTargetD3D : public PLSRenderTarget +{ +public: + ~PLSRenderTargetD3D() override {} + + void setTargetTexture(ID3D11Device*, ComPtr<ID3D11Texture2D> tex); + ID3D11Texture2D* targetTexture() const { return m_targetTexture.Get(); } + +private: + friend class PLSRenderContextD3D; + + PLSRenderTargetD3D(ID3D11Device*, size_t width, size_t height); + + ComPtr<ID3D11Texture2D> m_targetTexture; + ComPtr<ID3D11Texture2D> m_coverageTexture; + ComPtr<ID3D11Texture2D> m_originalDstColorTexture; + ComPtr<ID3D11Texture2D> m_clipTexture; + + ComPtr<ID3D11UnorderedAccessView> m_targetUAV; + ComPtr<ID3D11UnorderedAccessView> m_coverageUAV; + ComPtr<ID3D11UnorderedAccessView> m_originalDstColorUAV; + ComPtr<ID3D11UnorderedAccessView> m_clipUAV; +}; + +// D3D backend implementation of PLSRenderContext. +class PLSRenderContextD3D : public PLSRenderContext +{ +public: + PLSRenderContextD3D(ComPtr<ID3D11Device>, ComPtr<ID3D11DeviceContext>, bool isIntel); + + rcp<PLSRenderTargetD3D> makeRenderTarget(size_t width, size_t height); + +private: + std::unique_ptr<BufferRingImpl> makeVertexBufferRing(size_t capacity, + size_t itemSizeInBytes) override; + + std::unique_ptr<TexelBufferRing> makeTexelBufferRing(TexelBufferRing::Format, + Renderable, + size_t widthInItems, + size_t height, + size_t texelsPerItem, + int textureIdx, + TexelBufferRing::Filter) override; + + std::unique_ptr<BufferRingImpl> makeUniformBufferRing(size_t capacity, + size_t itemSizeInBytes) override; + + void allocateTessellationTexture(size_t height) override; + + void onFlush(FlushType, + LoadAction, + size_t gradSpanCount, + size_t gradSpansHeight, + size_t tessVertexSpanCount, + size_t tessDataHeight, + bool needsClipBuffer) override; + + void setPipelineLayoutAndShaders(DrawType, const ShaderFeatures&); + + const bool m_isIntel; + + ComPtr<ID3D11Device> m_gpu; + ComPtr<ID3D11DeviceContext> m_gpuContext; + ComPtr<ID3D11Texture2D> m_tessTexture; + ComPtr<ID3D11ShaderResourceView> m_tessTextureSRV; + ComPtr<ID3D11RenderTargetView> m_tessTextureRTV; + + ComPtr<ID3D11RasterizerState> m_rasterState; + ComPtr<ID3D11RasterizerState> m_debugWireframeState; + + ComPtr<ID3D11InputLayout> m_colorRampLayout; + ComPtr<ID3D11VertexShader> m_colorRampVertexShader; + ComPtr<ID3D11PixelShader> m_colorRampPixelShader; + + ComPtr<ID3D11InputLayout> m_tessellateLayout; + ComPtr<ID3D11VertexShader> m_tessellateVertexShader; + ComPtr<ID3D11PixelShader> m_tessellatePixelShader; + + struct DrawVertexShader + { + ComPtr<ID3D11InputLayout> layout; + ComPtr<ID3D11VertexShader> shader; + }; + std::map<uint32_t, DrawVertexShader> m_drawVertexShaders; + std::map<uint32_t, ComPtr<ID3D11PixelShader>> m_drawPixelShaders; + + ComPtr<ID3D11Buffer> m_patchVertexBuffer; + ComPtr<ID3D11Buffer> m_patchIndexBuffer; + + struct PerDrawUniforms + { + PerDrawUniforms(uint32_t baseInstance_) : baseInstance(baseInstance_) {} + uint32_t baseInstance; + uint32_t pad0; + uint32_t pad1; + uint32_t pad2; + }; + static_assert(sizeof(PerDrawUniforms) == 16); + + ComPtr<ID3D11Buffer> m_perDrawUniforms; + + ComPtr<ID3D11SamplerState> m_gradSampler; +}; +} // namespace rive::pls
diff --git a/include/rive/pls/gl/pls_render_context_gl.hpp b/include/rive/pls/gl/pls_render_context_gl.hpp index d04ad89..2098c47 100644 --- a/include/rive/pls/gl/pls_render_context_gl.hpp +++ b/include/rive/pls/gl/pls_render_context_gl.hpp
@@ -93,8 +93,8 @@ class PLSImplWebGL; class PLSImplRWTexture; - static std::unique_ptr<PLSImpl> MakePLSImplEXTNative(); - static std::unique_ptr<PLSImpl> MakePLSImplFramebufferFetch(GLExtensions); + static std::unique_ptr<PLSImpl> MakePLSImplEXTNative(const GLExtensions&); + static std::unique_ptr<PLSImpl> MakePLSImplFramebufferFetch(const GLExtensions&); static std::unique_ptr<PLSImpl> MakePLSImplWebGL(); static std::unique_ptr<PLSImpl> MakePLSImplRWTexture(); @@ -129,6 +129,7 @@ size_t itemSizeInBytes) override; std::unique_ptr<TexelBufferRing> makeTexelBufferRing(TexelBufferRing::Format, + Renderable, size_t widthInItems, size_t height, size_t texelsPerItem,
diff --git a/include/rive/pls/metal/pls_render_context_metal.h b/include/rive/pls/metal/pls_render_context_metal.h index afaf3f8..ef3ef48 100644 --- a/include/rive/pls/metal/pls_render_context_metal.h +++ b/include/rive/pls/metal/pls_render_context_metal.h
@@ -34,6 +34,7 @@ size_t itemSizeInBytes) override; std::unique_ptr<TexelBufferRing> makeTexelBufferRing(TexelBufferRing::Format, + Renderable, size_t widthInItems, size_t height, size_t texelsPerItem,
diff --git a/include/rive/pls/pls.hpp b/include/rive/pls/pls.hpp index eea399e..83ce78d 100644 --- a/include/rive/pls/pls.hpp +++ b/include/rive/pls/pls.hpp
@@ -339,6 +339,7 @@ uint32_t segmentCounts; // [joinSegmentCount, polarSegmentCount, parametricSegmentCount] uint32_t contourIDWithFlags; // flags | contourID }; +static_assert(sizeof(TessVertexSpan) == sizeof(float) * 16); // Per-vertex data for shaders that draw triangles. struct TriangleVertex @@ -350,7 +351,7 @@ Vec2D point; int32_t weight_pathID; // [(weight << 16] }; -static_assert(sizeof(TriangleVertex) == 3 * sizeof(float)); +static_assert(sizeof(TriangleVertex) == sizeof(float) * 3); // Once all curves in a contour have been tessellated, we render the tessellated vertices in // "patches" (aka specific instanced geometry). @@ -404,17 +405,16 @@ constexpr static uint32_t kMidpointFanPatchIndexCount = kMidpointFanPatchSegmentSpan * 6 /*AA outer ramp*/ + (kMidpointFanPatchSegmentSpan - 1) * 3 /*Curve fan*/ + 3 /*Triangle from path midpoint*/; -constexpr static uint32_t kMidpointFanPatchIndexOffset = 0; -static_assert(kMidpointFanPatchIndexOffset % 4 == 0); +constexpr static uint32_t kMidpointFanPatchBaseIndex = 0; +static_assert((kMidpointFanPatchBaseIndex * sizeof(uint16_t)) % 4 == 0); constexpr static uint32_t kOuterCurvePatchVertexCount = (kOuterCurvePatchSegmentSpan + 1) * 3 /*AA center ramp with bowtie*/ + kOuterCurvePatchSegmentSpan /*Curve fan*/; constexpr static uint32_t kOuterCurvePatchIndexCount = kOuterCurvePatchSegmentSpan * 12 /*AA center ramp with bowtie*/ + (kOuterCurvePatchSegmentSpan - 2) * 3 /*Curve fan*/; -constexpr static uint32_t kOuterCurvePatchIndexOffset = - kMidpointFanPatchIndexCount * sizeof(uint16_t); -static_assert(kOuterCurvePatchIndexOffset % 4 == 0); +constexpr static uint32_t kOuterCurvePatchBaseIndex = kMidpointFanPatchIndexCount; +static_assert((kOuterCurvePatchBaseIndex * sizeof(uint16_t)) % 4 == 0); constexpr static uint32_t kPatchVertexBufferCount = kMidpointFanPatchVertexCount + kOuterCurvePatchVertexCount; constexpr static uint32_t kPatchIndexBufferCount =
diff --git a/include/rive/pls/pls_render_context.hpp b/include/rive/pls/pls_render_context.hpp index 5edec76..f1a3916 100644 --- a/include/rive/pls/pls_render_context.hpp +++ b/include/rive/pls/pls_render_context.hpp
@@ -259,7 +259,14 @@ virtual std::unique_ptr<BufferRingImpl> makeVertexBufferRing(size_t capacity, size_t itemSizeInBytes) = 0; + enum class Renderable : bool + { + no, + yes + }; + virtual std::unique_ptr<TexelBufferRing> makeTexelBufferRing(TexelBufferRing::Format, + Renderable, size_t widthInItems, size_t height, size_t texelsPerItem, @@ -378,14 +385,14 @@ } } - constexpr static uintptr_t PatchIndexOffset(DrawType drawType) + constexpr static uintptr_t PatchBaseIndex(DrawType drawType) { switch (drawType) { case DrawType::midpointFanPatches: - return kMidpointFanPatchIndexOffset; + return kMidpointFanPatchBaseIndex; case DrawType::outerCurvePatches: - return kOuterCurvePatchIndexOffset; + return kOuterCurvePatchBaseIndex; default: RIVE_UNREACHABLE(); }
diff --git a/out/premake5.lua b/out/premake5.lua index 173da02..01659e3 100644 --- a/out/premake5.lua +++ b/out/premake5.lua
@@ -37,7 +37,10 @@ RIVE_RUNTIME_DIR .. "/skia/dependencies/glfw/include"} flags { "FatalWarnings" } - files {"../path_fiddle/*.cpp"} + files { + "../path_fiddle/path_fiddle.cpp", + "../path_fiddle/fiddle_context_gl.cpp", + } links {"rive", "rive_pls_renderer", "rive_harfbuzz", "rive_sheenbidi"} @@ -53,15 +56,16 @@ filter "system:windows" do + files {"../path_fiddle/fiddle_context_d3d.cpp"} architecture "x64" defines {"RIVE_WINDOWS", "_CRT_SECURE_NO_WARNINGS"} libdirs {RIVE_RUNTIME_DIR .. "/skia/dependencies/glfw_build/src/Release"} - links {"glfw3", "opengl32"} + links {"glfw3", "opengl32", "d3d11", "dxgi", "d3dcompiler"} end filter "system:macosx" do - files {"../path_fiddle/*.mm"} + files {"../path_fiddle/fiddle_context_metal.mm"} buildoptions {"-fobjc-arc"} links {"glfw3", "Cocoa.framework",
diff --git a/out/premake5_pls_renderer.lua b/out/premake5_pls_renderer.lua index d96670f..abb78aa 100644 --- a/out/premake5_pls_renderer.lua +++ b/out/premake5_pls_renderer.lua
@@ -156,6 +156,7 @@ filter "system:windows" do architecture "x64" + files {"../renderer/d3d/*.cpp"} end if os.host() == 'macosx'
diff --git a/path_fiddle/fiddle_context.hpp b/path_fiddle/fiddle_context.hpp index 47d699d..99ab6c8 100644 --- a/path_fiddle/fiddle_context.hpp +++ b/path_fiddle/fiddle_context.hpp
@@ -10,6 +10,7 @@ constexpr static int kZoomWindowWidth = 70, kZoomWindowHeight = 42, kZoomWindowScale = 16; +extern bool g_preferIntelGPU; extern struct GLFWwindow* g_window; extern bool g_wireframe; extern bool g_disableFill; @@ -33,4 +34,7 @@ #ifdef __APPLE__ static std::unique_ptr<FiddleContext> MakeMetalPLS(); #endif +#ifdef _WIN32 + static std::unique_ptr<FiddleContext> MakeD3DPLS(); +#endif };
diff --git a/path_fiddle/fiddle_context_d3d.cpp b/path_fiddle/fiddle_context_d3d.cpp new file mode 100644 index 0000000..4838079 --- /dev/null +++ b/path_fiddle/fiddle_context_d3d.cpp
@@ -0,0 +1,147 @@ +#include "fiddle_context.hpp" + +#include "rive/pls/pls_factory.hpp" +#include "rive/pls/pls_renderer.hpp" +#include "rive/pls/d3d/pls_render_context_d3d.hpp" +#include "rive/pls/d3d/d3d11.hpp" +#include <array> +#include <dxgi1_2.h> + +#define GLFW_INCLUDE_NONE +#define GLFW_EXPOSE_NATIVE_WIN32 +#include "GLFW/glfw3.h" +#include <GLFW/glfw3native.h> + +using namespace rive; +using namespace rive::pls; + +class FiddleContextD3DPLS : public FiddleContext +{ +public: + FiddleContextD3DPLS(ComPtr<IDXGIFactory2> d3dFactory, + ComPtr<ID3D11Device> gpu, + ComPtr<ID3D11DeviceContext> gpuContext, + bool isIntel) : + m_d3dFactory(std::move(d3dFactory)), + m_gpu(std::move(gpu)), + m_gpuContext(std::move(gpuContext)), + m_plsContext(new PLSRenderContextD3D(m_gpu, m_gpuContext, isIntel)) + {} + + float dpiScale() const override { return 1; } + + std::unique_ptr<Factory> makeFactory() override { return std::make_unique<PLSFactory>(); } + + void onSizeChanged(int width, int height) override + { + m_swapchain.Reset(); + + DXGI_SWAP_CHAIN_DESC1 scd{}; + scd.Width = width; + scd.Height = height; + scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + scd.SampleDesc.Count = 1; + scd.BufferUsage = DXGI_USAGE_UNORDERED_ACCESS; + scd.BufferCount = 2; + scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + VERIFY_OK(m_d3dFactory->CreateSwapChainForHwnd(m_gpu.Get(), + glfwGetWin32Window(g_window), + &scd, + NULL, + NULL, + m_swapchain.GetAddressOf())); + + m_renderTarget = m_plsContext->makeRenderTarget(width, height); + } + + void toggleZoomWindow() override {} + + std::unique_ptr<Renderer> makeRenderer(int width, int height) override + { + return std::make_unique<PLSRenderer>(m_plsContext.get()); + } + + void begin() override + { + ComPtr<ID3D11Texture2D> backBuffer; + VERIFY_OK(m_swapchain->GetBuffer(0, + __uuidof(ID3D11Texture2D), + reinterpret_cast<void**>(backBuffer.GetAddressOf()))); + m_renderTarget->setTargetTexture(m_gpu.Get(), std::move(backBuffer)); + + PLSRenderContext::FrameDescriptor frameDescriptor; + frameDescriptor.renderTarget = m_renderTarget; + frameDescriptor.clearColor = 0xff404040; + frameDescriptor.wireframe = g_wireframe; + frameDescriptor.fillsDisabled = g_disableFill; + frameDescriptor.strokesDisabled = g_disableStroke; + m_plsContext->beginFrame(std::move(frameDescriptor)); + } + + void end() override + { + m_plsContext->flush(); + m_swapchain->Present(0, 0); + } + + void shrinkGPUResourcesToFit() final { m_plsContext->shrinkGPUResourcesToFit(); } + +private: + ComPtr<IDXGIFactory2> m_d3dFactory; + ComPtr<ID3D11Device> m_gpu; + ComPtr<ID3D11DeviceContext> m_gpuContext; + ComPtr<IDXGISwapChain1> m_swapchain; + std::unique_ptr<PLSRenderContextD3D> m_plsContext; + rcp<PLSRenderTargetD3D> m_renderTarget; +}; + +std::unique_ptr<FiddleContext> FiddleContext::MakeD3DPLS() +{ + // Create a DXGIFactory object. + ComPtr<IDXGIFactory2> factory; + VERIFY_OK(CreateDXGIFactory(__uuidof(IDXGIFactory2), + reinterpret_cast<void**>(factory.GetAddressOf()))); + + ComPtr<IDXGIAdapter> adapter; + DXGI_ADAPTER_DESC adapterDesc{}; + bool isIntel = false; + for (UINT i = 0; factory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND; ++i) + { + adapter->GetDesc(&adapterDesc); + isIntel = adapterDesc.VendorId == 0x163C || adapterDesc.VendorId == 0x8086 || + adapterDesc.VendorId == 0x8087; + if (isIntel == g_preferIntelGPU) + { + break; + } + } + + ComPtr<ID3D11Device> gpu; + ComPtr<ID3D11DeviceContext> gpuContext; + D3D_FEATURE_LEVEL featureLevels[] = {D3D_FEATURE_LEVEL_11_1}; + VERIFY_OK(D3D11CreateDevice(adapter.Get(), + D3D_DRIVER_TYPE_UNKNOWN, + NULL, +#ifdef DEBUG + D3D11_CREATE_DEVICE_DEBUG, +#else + 0, +#endif + featureLevels, + std::size(featureLevels), + D3D11_SDK_VERSION, + gpu.GetAddressOf(), + NULL, + gpuContext.GetAddressOf())); + if (!gpu || !gpuContext) + { + return nullptr; + } + + printf("D3D device: %S\n", adapterDesc.Description); + + return std::make_unique<FiddleContextD3DPLS>(std::move(factory), + std::move(gpu), + std::move(gpuContext), + isIntel); +}
diff --git a/path_fiddle/path_fiddle.cpp b/path_fiddle/path_fiddle.cpp index 4131c1c..498993e 100644 --- a/path_fiddle/path_fiddle.cpp +++ b/path_fiddle/path_fiddle.cpp
@@ -22,6 +22,7 @@ using namespace rive; +bool g_preferIntelGPU = false; GLFWwindow* g_window = nullptr; bool g_wireframe = false; bool g_disableFill = false; @@ -215,10 +216,18 @@ } } -bool metal = false; -bool angle = false; bool skia = false; +enum class API +{ + gl, + metal, + d3d +}; + +API api = API::gl; +bool angle = false; + int lastWidth = 0, lastHeight = 0; double fpsLastTime = glfwGetTime(); int fpsFrames = 0; @@ -266,17 +275,20 @@ { if (!strcmp(argv[i], "--gl")) { - metal = false; + api = API::gl; } else if (!strcmp(argv[i], "--metal")) { - metal = true; + api = API::metal; + } + else if (!strcmp(argv[i], "--d3d")) + { + api = API::d3d; } else if (!strcmp(argv[i], "--skia")) { skia = true; } -#ifdef RIVE_DESKTOP_GL else if (!strcmp(argv[i], "--angle_gl")) { glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_OPENGL); @@ -297,7 +309,10 @@ glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_METAL); angle = true; } -#endif + else if (!strcmp(argv[i], "--intel") || !strcmp(argv[i], "-i")) + { + g_preferIntelGPU = true; + } else if (sscanf(argv[i], "-a%i", &s_animation)) {} else if (sscanf(argv[i], "-h%i", &s_horzRepeat)) @@ -324,23 +339,30 @@ glfwWindowHint(GLFW_SAMPLES, 0); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - if (metal) + switch (api) { - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); + case API::metal: + case API::d3d: + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); + break; + case API::gl: + if (angle) + { + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + } + else + { + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + } + break; } - else if (angle) - { - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - } - else - { - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - } + glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_TRUE); + glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); g_window = glfwCreateWindow(1600, 1600, "Rive Renderer", nullptr, nullptr); if (!g_window) { @@ -354,27 +376,40 @@ glfwSetCursorPosCallback(g_window, mousemove_callback); glfwSetKeyCallback(g_window, key_callback); glfwMakeContextCurrent(g_window); + glfwShowWindow(g_window); - if (metal) + switch (api) { - if (skia) - { - fprintf(stderr, "Skia not supported on Metal yet.\n"); - exit(-1); - } + case API::metal: + if (skia) + { + fprintf(stderr, "Skia not supported on Metal yet.\n"); + break; + } #ifdef __APPLE__ - s_fiddleContext = FiddleContext::MakeMetalPLS(); + s_fiddleContext = FiddleContext::MakeMetalPLS(); #endif - } - else if (skia) - { + break; + case API::d3d: + if (skia) + { + fprintf(stderr, "Skia not supported on d3d yet.\n"); + break; + } +#ifdef _WIN32 + s_fiddleContext = FiddleContext::MakeD3DPLS(); +#endif + break; + case API::gl: + if (skia) + { #ifdef RIVE_SKIA - s_fiddleContext = FiddleContext::MakeGLSkia(); + s_fiddleContext = FiddleContext::MakeGLSkia(); #endif - } - else - { - s_fiddleContext = FiddleContext::MakeGLPLS(); + break; + } + s_fiddleContext = FiddleContext::MakeGLPLS(); + break; } if (!s_fiddleContext) {
diff --git a/renderer/d3d/pls_render_context_d3d.cpp b/renderer/d3d/pls_render_context_d3d.cpp new file mode 100644 index 0000000..c2f89c8 --- /dev/null +++ b/renderer/d3d/pls_render_context_d3d.cpp
@@ -0,0 +1,830 @@ +/* + * Copyright 2023 Rive + */ + +#include "rive/pls/d3d/pls_render_context_d3d.hpp" + +#include <D3DCompiler.h> +#include <sstream> + +#include "../out/obj/generated/advanced_blend.glsl.hpp" +#include "../out/obj/generated/color_ramp.glsl.hpp" +#include "../out/obj/generated/common.glsl.hpp" +#include "../out/obj/generated/draw.glsl.hpp" +#include "../out/obj/generated/hlsl.glsl.hpp" +#include "../out/obj/generated/tessellate.glsl.hpp" + +constexpr static UINT kPatchVertexDataSlot = 0; +constexpr static UINT kTriangleVertexDataSlot = 1; + +namespace rive::pls +{ +static DXGI_FORMAT d3d_format(TexelBufferRing::Format format) +{ + + switch (format) + { + case TexelBufferRing::Format::rgba32f: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case TexelBufferRing::Format::rgba32ui: + return DXGI_FORMAT_R32G32B32A32_UINT; + case TexelBufferRing::Format::rgba8: + return DXGI_FORMAT_R8G8B8A8_UNORM; + } +} + +static ComPtr<ID3D11Texture2D> make_simple_2d_texture(ID3D11Device* gpu, + DXGI_FORMAT format, + size_t width, + size_t height, + UINT bindFlags) +{ + D3D11_TEXTURE2D_DESC desc{}; + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + ComPtr<ID3D11Texture2D> tex; + VERIFY_OK(gpu->CreateTexture2D(&desc, NULL, tex.GetAddressOf())); + return tex; +} + +static ComPtr<ID3D11ShaderResourceView> make_simple_2d_srv(ID3D11Device* gpu, + ID3D11Texture2D* tex2D, + DXGI_FORMAT format) +{ + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = format; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = 1; + + ComPtr<ID3D11ShaderResourceView> srv; + VERIFY_OK(gpu->CreateShaderResourceView(tex2D, &srvDesc, srv.GetAddressOf())); + return srv; +} + +static ComPtr<ID3D11RenderTargetView> make_simple_2d_rtv(ID3D11Device* gpu, + ID3D11Texture2D* tex2D, + DXGI_FORMAT format) +{ + D3D11_RENDER_TARGET_VIEW_DESC rtDesc; + rtDesc.Format = format; + rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtDesc.Texture2D.MipSlice = 0; + + ComPtr<ID3D11RenderTargetView> rtv; + VERIFY_OK(gpu->CreateRenderTargetView(tex2D, &rtDesc, &rtv)); + return rtv; +} + +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.GetAddressOf())); + return uav; +} + +static ComPtr<ID3DBlob> compile_source_to_blob(ID3D11Device* gpu, + const char* shaderTypeDefineName, + const std::string& commonSource, + const char* entrypoint, + const char* target) +{ + std::ostringstream source; + source << "#define " << shaderTypeDefineName << '\n'; + source << commonSource; + + const std::string& sourceStr = source.str(); + ComPtr<ID3DBlob> blob; + ComPtr<ID3DBlob> errors; + HRESULT hr = D3DCompile(sourceStr.c_str(), + sourceStr.length(), + nullptr, + nullptr, + nullptr, + entrypoint, + target, + D3DCOMPILE_ENABLE_STRICTNESS, + 0, + &blob, + &errors); + if (errors && errors->GetBufferPointer()) + { + fprintf(stderr, "Errors or warnings compiling shader.\n"); + int l = 1; + std::stringstream stream(sourceStr); + std::string lineStr; + while (std::getline(stream, lineStr, '\n')) + { + fprintf(stderr, "%4i| %s\n", l++, lineStr.c_str()); + } + fprintf(stderr, "%s\n", reinterpret_cast<char*>(errors->GetBufferPointer())); + exit(-1); + } + if (FAILED(hr)) + { + fprintf(stderr, "Failed to compile shader.\n"); + exit(-1); + } + return blob; +} + +PLSRenderContextD3D::PLSRenderContextD3D(ComPtr<ID3D11Device> gpu, + ComPtr<ID3D11DeviceContext> gpuContext, + bool isIntel) : + PLSRenderContext(PlatformFeatures()), m_isIntel(isIntel), m_gpu(gpu), m_gpuContext(gpuContext) +{ + D3D11_RASTERIZER_DESC rasterDesc; + rasterDesc.FillMode = D3D11_FILL_SOLID; + rasterDesc.CullMode = D3D11_CULL_NONE; + 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 = FALSE; + rasterDesc.ScissorEnable = FALSE; + rasterDesc.MultisampleEnable = FALSE; + rasterDesc.AntialiasedLineEnable = FALSE; + VERIFY_OK(m_gpu->CreateRasterizerState(&rasterDesc, m_rasterState.GetAddressOf())); + m_gpuContext->RSSetState(m_rasterState.Get()); + + // Make one more raster state for wireframe, for debugging purposes. + rasterDesc.FillMode = D3D11_FILL_WIREFRAME; + VERIFY_OK(m_gpu->CreateRasterizerState(&rasterDesc, m_debugWireframeState.GetAddressOf())); + + // Compile the shaders that render gradient color ramps. + { + std::ostringstream s; + s << glsl::hlsl << '\n'; + s << glsl::common << '\n'; + s << glsl::color_ramp << '\n'; + ComPtr<ID3DBlob> vertexBlob = compile_source_to_blob(m_gpu.Get(), + GLSL_VERTEX, + s.str().c_str(), + GLSL_colorRampVertexMain, + "vs_5_0"); + ComPtr<ID3DBlob> pixelBlob = compile_source_to_blob(m_gpu.Get(), + GLSL_FRAGMENT, + s.str().c_str(), + GLSL_colorRampFragmentMain, + "ps_5_0"); + D3D11_INPUT_ELEMENT_DESC spanDesc = + {GLSL_a_span, 0, DXGI_FORMAT_R32G32B32A32_UINT, 0, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1}; + VERIFY_OK(m_gpu->CreateInputLayout(&spanDesc, + 1, + vertexBlob->GetBufferPointer(), + vertexBlob->GetBufferSize(), + &m_colorRampLayout)); + VERIFY_OK(m_gpu->CreateVertexShader(vertexBlob->GetBufferPointer(), + vertexBlob->GetBufferSize(), + nullptr, + &m_colorRampVertexShader)); + VERIFY_OK(m_gpu->CreatePixelShader(pixelBlob->GetBufferPointer(), + pixelBlob->GetBufferSize(), + nullptr, + &m_colorRampPixelShader)); + } + + // Compile the tessellation shaders. + { + std::ostringstream s; + s << glsl::hlsl << '\n'; + s << glsl::common << '\n'; + s << glsl::tessellate << '\n'; + ComPtr<ID3DBlob> vertexBlob = compile_source_to_blob(m_gpu.Get(), + GLSL_VERTEX, + s.str().c_str(), + GLSL_tessellateVertexMain, + "vs_5_0"); + ComPtr<ID3DBlob> pixelBlob = compile_source_to_blob(m_gpu.Get(), + GLSL_FRAGMENT, + s.str().c_str(), + GLSL_tessellateFragmentMain, + "ps_5_0"); + 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_joinTangent, + 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(m_gpu->CreateInputLayout(attribsDesc, + std::size(attribsDesc), + vertexBlob->GetBufferPointer(), + vertexBlob->GetBufferSize(), + &m_tessellateLayout)); + VERIFY_OK(m_gpu->CreateVertexShader(vertexBlob->GetBufferPointer(), + vertexBlob->GetBufferSize(), + nullptr, + &m_tessellateVertexShader)); + VERIFY_OK(m_gpu->CreatePixelShader(pixelBlob->GetBufferPointer(), + pixelBlob->GetBufferSize(), + nullptr, + &m_tessellatePixelShader)); + } + + // Set up the path patch rendering buffers. + PatchVertex patchVertices[kPatchVertexBufferCount]; + uint16_t patchIndices[kPatchIndexBufferCount]; + GeneratePatchBufferData(patchVertices, patchIndices); + + { + D3D11_BUFFER_DESC vertexBufferDesc{}; + vertexBufferDesc.ByteWidth = sizeof(patchVertices); + vertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; + vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + vertexBufferDesc.StructureByteStride = sizeof(PatchVertex); + + D3D11_SUBRESOURCE_DATA vertexDataDesc{}; + vertexDataDesc.pSysMem = patchVertices; + + VERIFY_OK(gpu->CreateBuffer(&vertexBufferDesc, + &vertexDataDesc, + m_patchVertexBuffer.GetAddressOf())); + } + + { + D3D11_BUFFER_DESC indexBufferDesc{}; + indexBufferDesc.ByteWidth = sizeof(patchIndices); + indexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; + indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + + D3D11_SUBRESOURCE_DATA indexDataDesc{}; + indexDataDesc.pSysMem = patchIndices; + + VERIFY_OK( + gpu->CreateBuffer(&indexBufferDesc, &indexDataDesc, m_patchIndexBuffer.GetAddressOf())); + } + + // Create a buffer for the uniforms that get updated every draw. + { + D3D11_BUFFER_DESC desc{}; + desc.ByteWidth = sizeof(PerDrawUniforms); + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.StructureByteStride = sizeof(PerDrawUniforms); + VERIFY_OK(gpu->CreateBuffer(&desc, nullptr, m_perDrawUniforms.GetAddressOf())); + } + + // Create a sampler for the gradient texture. + D3D11_SAMPLER_DESC gradSamplerDesc; + gradSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + gradSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + gradSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + gradSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + gradSamplerDesc.MipLODBias = 0.0f; + gradSamplerDesc.MaxAnisotropy = 1; + gradSamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + gradSamplerDesc.MinLOD = 0; + gradSamplerDesc.MaxLOD = 0; + VERIFY_OK(m_gpu->CreateSamplerState(&gradSamplerDesc, m_gradSampler.GetAddressOf())); + m_gpuContext->PSSetSamplers(0, 1, m_gradSampler.GetAddressOf()); +} + +class BufferRingD3D : public BufferRingShadowImpl +{ +public: + BufferRingD3D(ID3D11Device* gpu, + ComPtr<ID3D11DeviceContext> gpuContext, + size_t capacity, + size_t itemSizeInBytes, + UINT bindFlags) : + BufferRingShadowImpl(capacity, itemSizeInBytes), m_gpuContext(gpuContext) + { + D3D11_BUFFER_DESC desc{}; + desc.ByteWidth = itemSizeInBytes * capacity; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = 0; + desc.StructureByteStride = itemSizeInBytes; + + for (size_t i = 0; i < kBufferRingSize; ++i) + { + VERIFY_OK(gpu->CreateBuffer(&desc, nullptr, m_buffers[i].GetAddressOf())); + } + } + + ID3D11Buffer* submittedBuffer() const { return m_buffers[submittedBufferIdx()].Get(); } + +protected: + void onUnmapAndSubmitBuffer(int bufferIdx, size_t bytesWritten) + { + if (bytesWritten == capacity() * itemSizeInBytes()) + { + // Constant buffers don't allow partial updates, so special-case the event where we + // update the entire buffer. + m_gpuContext + ->UpdateSubresource(m_buffers[bufferIdx].Get(), 0, NULL, shadowBuffer(), 0, 0); + } + else + { + D3D11_BOX box; + box.left = 0; + box.right = bytesWritten; + box.top = 0; + box.bottom = 1; + box.front = 0; + box.back = 1; + m_gpuContext + ->UpdateSubresource(m_buffers[bufferIdx].Get(), 0, &box, shadowBuffer(), 0, 0); + } + } + + ComPtr<ID3D11DeviceContext> m_gpuContext; + ComPtr<ID3D11Buffer> m_buffers[kBufferRingSize]; +}; + +std::unique_ptr<BufferRingImpl> PLSRenderContextD3D::makeVertexBufferRing(size_t capacity, + size_t itemSizeInBytes) +{ + return std::make_unique<BufferRingD3D>(m_gpu.Get(), + m_gpuContext, + capacity, + itemSizeInBytes, + D3D11_BIND_VERTEX_BUFFER); +} + +std::unique_ptr<BufferRingImpl> PLSRenderContextD3D::makeUniformBufferRing(size_t capacity, + size_t itemSizeInBytes) +{ + return std::make_unique<BufferRingD3D>(m_gpu.Get(), + m_gpuContext, + capacity, + itemSizeInBytes, + D3D11_BIND_CONSTANT_BUFFER); +} + +class TexelBufferD3D : public TexelBufferRing +{ +public: + TexelBufferD3D(ID3D11Device* gpu, + ComPtr<ID3D11DeviceContext> gpuContext, + Format format, + bool renderable, + size_t widthInItems, + size_t height, + size_t texelsPerItem) : + TexelBufferRing(format, widthInItems, height, texelsPerItem), m_gpuContext(gpuContext) + { + DXGI_FORMAT formatD3D = d3d_format(format); + UINT bindFlags = D3D11_BIND_SHADER_RESOURCE; + if (renderable) + { + bindFlags |= D3D11_BIND_RENDER_TARGET; + } + for (size_t i = 0; i < kBufferRingSize; ++i) + { + m_textures[i] = + make_simple_2d_texture(gpu, formatD3D, widthInTexels(), height, bindFlags); + m_srvs[i] = make_simple_2d_srv(gpu, m_textures[i].Get(), formatD3D); + if (renderable) + { + m_rtvs[i] = make_simple_2d_rtv(gpu, m_textures[i].Get(), formatD3D); + } + } + } + + ID3D11ShaderResourceView* submittedSRV() const { return m_srvs[submittedBufferIdx()].Get(); } + ID3D11RenderTargetView* submittedRenderTargetView() const + { + return m_rtvs[submittedBufferIdx()].Get(); + } + +private: + void submitTexels(int textureIdx, size_t updateWidthInTexels, size_t updateHeight) override + { + D3D11_BOX box; + box.left = 0; + box.right = updateWidthInTexels; + box.top = 0; + box.bottom = updateHeight; + box.front = 0; + box.back = 1; + m_gpuContext->UpdateSubresource(m_textures[textureIdx].Get(), + 0, + &box, + shadowBuffer(), + widthInItems() * itemSizeInBytes(), + 0); + } + + ComPtr<ID3D11DeviceContext> m_gpuContext; + ComPtr<ID3D11Texture2D> m_textures[kBufferRingSize]; + ComPtr<ID3D11ShaderResourceView> m_srvs[kBufferRingSize]; + ComPtr<ID3D11RenderTargetView> m_rtvs[kBufferRingSize]; +}; + +std::unique_ptr<TexelBufferRing> PLSRenderContextD3D::makeTexelBufferRing( + TexelBufferRing::Format format, + Renderable renderable, + size_t widthInItems, + size_t height, + size_t texelsPerItem, + int textureIdx, + TexelBufferRing::Filter) +{ + return std::make_unique<TexelBufferD3D>(m_gpu.Get(), + m_gpuContext, + format, + renderable == Renderable::yes, + widthInItems, + height, + texelsPerItem); +} + +PLSRenderTargetD3D::PLSRenderTargetD3D(ID3D11Device* gpu, size_t width, size_t height) : + PLSRenderTarget(width, height) +{ + m_coverageTexture = make_simple_2d_texture(gpu, + DXGI_FORMAT_R32_UINT, + width, + height, + D3D11_BIND_UNORDERED_ACCESS); + m_originalDstColorTexture = make_simple_2d_texture(gpu, + DXGI_FORMAT_R8G8B8A8_UNORM, + width, + height, + D3D11_BIND_UNORDERED_ACCESS); + m_clipTexture = make_simple_2d_texture(gpu, + DXGI_FORMAT_R32_UINT, + width, + height, + D3D11_BIND_UNORDERED_ACCESS); + + m_coverageUAV = make_simple_2d_uav(gpu, m_coverageTexture.Get(), DXGI_FORMAT_R32_UINT); + m_originalDstColorUAV = + make_simple_2d_uav(gpu, m_originalDstColorTexture.Get(), DXGI_FORMAT_R8G8B8A8_UNORM); + m_clipUAV = make_simple_2d_uav(gpu, m_clipTexture.Get(), DXGI_FORMAT_R32_UINT); +} + +void PLSRenderTargetD3D::setTargetTexture(ID3D11Device* gpu, ComPtr<ID3D11Texture2D> tex) +{ +#ifdef DEBUG + D3D11_TEXTURE2D_DESC desc; + tex->GetDesc(&desc); + assert(desc.Width == width()); + assert(desc.Height == height()); + assert(desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM); +#endif + m_targetTexture = std::move(tex); + m_targetUAV = make_simple_2d_uav(gpu, m_targetTexture.Get(), DXGI_FORMAT_R8G8B8A8_UNORM); +} + +rcp<PLSRenderTargetD3D> PLSRenderContextD3D::makeRenderTarget(size_t width, size_t height) +{ + return rcp(new PLSRenderTargetD3D(m_gpu.Get(), width, height)); +} + +void PLSRenderContextD3D::allocateTessellationTexture(size_t height) +{ + m_tessTexture = make_simple_2d_texture(m_gpu.Get(), + DXGI_FORMAT_R32G32B32A32_UINT, + kTessTextureWidth, + height, + D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); + m_tessTextureSRV = + make_simple_2d_srv(m_gpu.Get(), m_tessTexture.Get(), DXGI_FORMAT_R32G32B32A32_UINT); + m_tessTextureRTV = + make_simple_2d_rtv(m_gpu.Get(), m_tessTexture.Get(), DXGI_FORMAT_R32G32B32A32_UINT); +} + +void PLSRenderContextD3D::setPipelineLayoutAndShaders(DrawType drawType, + const ShaderFeatures& shaderFeatures) +{ + uint32_t vertexShaderKey = ShaderUniqueKey(SourceType::vertexOnly, drawType, shaderFeatures); + auto vertexEntry = m_drawVertexShaders.find(vertexShaderKey); + + uint32_t pixelShaderKey = ShaderUniqueKey(SourceType::wholeProgram, drawType, shaderFeatures); + auto pixelEntry = m_drawPixelShaders.find(pixelShaderKey); + + if (vertexEntry == m_drawVertexShaders.end() || pixelEntry == m_drawPixelShaders.end()) + { + std::ostringstream s; + uint64_t shaderFeatureDefines = + shaderFeatures.getPreprocessorDefines(SourceType::wholeProgram); + if (drawType == DrawType::interiorTriangulation) + { + s << "#define " << GLSL_DRAW_INTERIOR_TRIANGLES << '\n'; + } + if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_ADVANCED_BLEND) + { + s << "#define " << GLSL_ENABLE_ADVANCED_BLEND << '\n'; + } + if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_PATH_CLIPPING) + { + s << "#define " << GLSL_ENABLE_PATH_CLIPPING << '\n'; + } + if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_EVEN_ODD) + { + s << "#define " << GLSL_ENABLE_EVEN_ODD << '\n'; + } + if (shaderFeatureDefines & ShaderFeatures::PreprocessorDefines::ENABLE_HSL_BLEND_MODES) + { + s << "#define " << GLSL_ENABLE_HSL_BLEND_MODES << '\n'; + } + s << "#define " << GLSL_ENABLE_BASE_INSTANCE_POLYFILL << '\n'; + s << glsl::hlsl << '\n'; + s << glsl::common << '\n'; + if (shaderFeatures.programFeatures.blendTier != BlendTier::srcOver) + { + s << glsl::advanced_blend << '\n'; + } + s << glsl::draw << '\n'; + + const std::string shader = s.str(); + + if (vertexEntry == m_drawVertexShaders.end()) + { + DrawVertexShader drawVertexShader; + ComPtr<ID3DBlob> blob = compile_source_to_blob(m_gpu.Get(), + GLSL_VERTEX, + shader.c_str(), + GLSL_drawVertexMain, + "vs_5_0"); + D3D11_INPUT_ELEMENT_DESC layoutDesc; + switch (drawType) + { + case DrawType::midpointFanPatches: + case DrawType::outerCurvePatches: + layoutDesc = {GLSL_a_patchVertexData, + 0, + DXGI_FORMAT_R32G32B32A32_FLOAT, + kPatchVertexDataSlot, + 0, + D3D11_INPUT_PER_VERTEX_DATA, + 0}; + break; + case DrawType::interiorTriangulation: + layoutDesc = {GLSL_a_triangleVertex, + 0, + DXGI_FORMAT_R32G32B32_FLOAT, + kTriangleVertexDataSlot, + 0, + D3D11_INPUT_PER_VERTEX_DATA, + 0}; + break; + } + VERIFY_OK(m_gpu->CreateInputLayout(&layoutDesc, + 1, + blob->GetBufferPointer(), + blob->GetBufferSize(), + &drawVertexShader.layout)); + VERIFY_OK(m_gpu->CreateVertexShader(blob->GetBufferPointer(), + blob->GetBufferSize(), + nullptr, + &drawVertexShader.shader)); + vertexEntry = m_drawVertexShaders.insert({vertexShaderKey, drawVertexShader}).first; + } + + if (pixelEntry == m_drawPixelShaders.end()) + { + ComPtr<ID3D11PixelShader> pixelShader; + ComPtr<ID3DBlob> blob = compile_source_to_blob(m_gpu.Get(), + GLSL_FRAGMENT, + shader.c_str(), + GLSL_drawFragmentMain, + "ps_5_0"); + VERIFY_OK(m_gpu->CreatePixelShader(blob->GetBufferPointer(), + blob->GetBufferSize(), + nullptr, + &pixelShader)); + pixelEntry = m_drawPixelShaders.insert({pixelShaderKey, pixelShader}).first; + } + } + + m_gpuContext->IASetInputLayout(vertexEntry->second.layout.Get()); + m_gpuContext->VSSetShader(vertexEntry->second.shader.Get(), NULL, 0); + m_gpuContext->PSSetShader(pixelEntry->second.Get(), NULL, 0); +} + +static ID3D11Buffer* submitted_buffer(const BufferRingImpl* bufferRing) +{ + return static_cast<const BufferRingD3D*>(bufferRing)->submittedBuffer(); +} + +static ID3D11ShaderResourceView* submitted_srv(const TexelBufferRing* texelBufferRing) +{ + return static_cast<const TexelBufferD3D*>(texelBufferRing)->submittedSRV(); +} + +static ID3D11RenderTargetView* submitted_rtv(const TexelBufferRing* texelBufferRing) +{ + return static_cast<const TexelBufferD3D*>(texelBufferRing)->submittedRenderTargetView(); +} + +void PLSRenderContextD3D::onFlush(FlushType flushType, + LoadAction loadAction, + size_t gradSpanCount, + size_t gradSpansHeight, + size_t tessVertexSpanCount, + size_t tessDataHeight, + bool needsClipBuffer) +{ + auto renderTarget = + static_cast<const PLSRenderTargetD3D*>(frameDescriptor().renderTarget.get()); + + constexpr static UINT kZero[4]{}; + if (loadAction == LoadAction::clear) + { + float clearColor4f[4]; + UnpackColorToRGBA32F(frameDescriptor().clearColor, clearColor4f); + m_gpuContext->ClearUnorderedAccessViewFloat(renderTarget->m_targetUAV.Get(), clearColor4f); + } + m_gpuContext->ClearUnorderedAccessViewUint(renderTarget->m_coverageUAV.Get(), kZero); + if (needsClipBuffer) + { + m_gpuContext->ClearUnorderedAccessViewUint(renderTarget->m_clipUAV.Get(), kZero); + } + + ID3D11Buffer* cbuffers[] = {submitted_buffer(uniformBufferRing()), m_perDrawUniforms.Get()}; + m_gpuContext->VSSetConstantBuffers(0, std::size(cbuffers), cbuffers); + + // Render the complex color ramps to the gradient texture. + if (gradSpanCount > 0) + { + ID3D11Buffer* gradSpanBuffer = submitted_buffer(gradSpanBufferRing()); + UINT gradStride = sizeof(GradientSpan); + UINT gradOffset = 0; + m_gpuContext->IASetVertexBuffers(0, 1, &gradSpanBuffer, &gradStride, &gradOffset); + m_gpuContext->IASetInputLayout(m_colorRampLayout.Get()); + m_gpuContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + m_gpuContext->VSSetShader(m_colorRampVertexShader.Get(), NULL, 0); + + D3D11_VIEWPORT viewport = {0, + static_cast<float>(gradTextureRowsForSimpleRamps()), + static_cast<float>(kGradTextureWidth), + static_cast<float>(gradSpansHeight), + 0, + 1}; + m_gpuContext->RSSetViewports(1, &viewport); + + m_gpuContext->PSSetShaderResources(0, 0, NULL); + m_gpuContext->PSSetShader(m_colorRampPixelShader.Get(), NULL, 0); + + ID3D11RenderTargetView* rtv = submitted_rtv(gradTexelBufferRing()); + m_gpuContext->OMSetRenderTargets(1, &rtv, NULL); + + m_gpuContext->DrawInstanced(4, gradSpanCount, 0, 0); + } + + // Tessellate all curves into vertices in the tessellation texture. + if (tessVertexSpanCount > 0) + { + ID3D11Buffer* tessSpanBuffer = submitted_buffer(tessSpanBufferRing()); + UINT tessStride = sizeof(TessVertexSpan); + UINT tessOffset = 0; + m_gpuContext->IASetVertexBuffers(0, 1, &tessSpanBuffer, &tessStride, &tessOffset); + m_gpuContext->IASetInputLayout(m_tessellateLayout.Get()); + m_gpuContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + m_gpuContext->VSSetShader(m_tessellateVertexShader.Get(), NULL, 0); + ID3D11ShaderResourceView* vsTextureViews[] = {NULL, + submitted_srv(pathBufferRing()), + submitted_srv(contourBufferRing())}; + static_assert(kTessVertexTextureIdx == 0); + static_assert(kPathTextureIdx == 1); + static_assert(kContourTextureIdx == 2); + m_gpuContext->VSSetShaderResources(0, std::size(vsTextureViews), vsTextureViews); + + D3D11_VIEWPORT viewport = + {0, 0, static_cast<float>(kTessTextureWidth), static_cast<float>(tessDataHeight), 0, 1}; + m_gpuContext->RSSetViewports(1, &viewport); + + m_gpuContext->PSSetShaderResources(0, 0, NULL); + m_gpuContext->PSSetShader(m_tessellatePixelShader.Get(), NULL, 0); + + m_gpuContext->OMSetRenderTargets(1, m_tessTextureRTV.GetAddressOf(), NULL); + + m_gpuContext->DrawInstanced(4, tessVertexSpanCount, 0, 0); + + if (m_isIntel) + { + // FIXME! Intel needs this flush! Driver bug? Find a lighter workaround? + m_gpuContext->Flush(); + } + } + + // Execute the DrawList. + ID3D11UnorderedAccessView* plsUAVs[] = {renderTarget->m_targetUAV.Get(), + renderTarget->m_coverageUAV.Get(), + renderTarget->m_originalDstColorUAV.Get(), + renderTarget->m_clipUAV.Get()}; + static_assert(kFramebufferPlaneIdx == 0); + static_assert(kCoveragePlaneIdx == 1); + static_assert(kOriginalDstColorPlaneIdx == 2); + static_assert(kClipPlaneIdx == 3); + m_gpuContext->OMSetRenderTargetsAndUnorderedAccessViews(0, + NULL, + NULL, + 0, + std::size(plsUAVs), + plsUAVs, + NULL); + + m_gpuContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + m_gpuContext->IASetIndexBuffer(m_patchIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0); + + ID3D11Buffer* vertexBuffers[] = {m_patchVertexBuffer.Get(), + submitted_buffer(triangleBufferRing())}; + static_assert(kPatchVertexDataSlot == 0); + static_assert(kTriangleVertexDataSlot == 1); + UINT vertexStrides[] = {sizeof(PatchVertex), sizeof(TriangleVertex)}; + UINT vertexOffsets[] = {0, 0}; + m_gpuContext->IASetVertexBuffers(0, 2, vertexBuffers, vertexStrides, vertexOffsets); + + ID3D11ShaderResourceView* vertexTextureViews[] = {m_tessTextureSRV.Get(), + submitted_srv(pathBufferRing()), + submitted_srv(contourBufferRing())}; + static_assert(kTessVertexTextureIdx == 0); + static_assert(kPathTextureIdx == 1); + static_assert(kContourTextureIdx == 2); + m_gpuContext->VSSetShaderResources(0, std::size(vertexTextureViews), vertexTextureViews); + + D3D11_VIEWPORT viewport = {0, + 0, + static_cast<float>(renderTarget->width()), + static_cast<float>(renderTarget->height()), + 0, + 1}; + m_gpuContext->RSSetViewports(1, &viewport); + if (frameDescriptor().wireframe) + { + m_gpuContext->RSSetState(m_debugWireframeState.Get()); + } + + ID3D11ShaderResourceView* gradTextureView = submitted_srv(gradTexelBufferRing()); + m_gpuContext->PSSetShaderResources(kGradTextureIdx, 1, &gradTextureView); + + for (const DrawList* draw = m_drawList; draw; draw = draw->next) + { + if (draw->vertexOrInstanceCount == 0) + { + continue; + } + + DrawType drawType = draw->drawType; + setPipelineLayoutAndShaders(drawType, draw->shaderFeatures); + + switch (drawType) + { + case DrawType::midpointFanPatches: + case DrawType::outerCurvePatches: + { + PerDrawUniforms uniforms(draw->baseVertexOrInstance); + m_gpuContext->UpdateSubresource(m_perDrawUniforms.Get(), 0, NULL, &uniforms, 0, 0); + m_gpuContext->DrawIndexedInstanced(PatchIndexCount(drawType), + draw->vertexOrInstanceCount, + PatchBaseIndex(drawType), + 0, + draw->baseVertexOrInstance); + break; + } + case DrawType::interiorTriangulation: + m_gpuContext->Draw(draw->vertexOrInstanceCount, draw->baseVertexOrInstance); + break; + } + } + + if (frameDescriptor().wireframe) + { + m_gpuContext->RSSetState(m_rasterState.Get()); + } +} +} // namespace rive::pls
diff --git a/renderer/gl/buffer_ring_gl.hpp b/renderer/gl/buffer_ring_gl.hpp index 5b5fc8a..6b41f99 100644 --- a/renderer/gl/buffer_ring_gl.hpp +++ b/renderer/gl/buffer_ring_gl.hpp
@@ -41,7 +41,7 @@ GLuint submittedTextureID() const { return m_ids[submittedBufferIdx()]; } protected: - void submitTexels(int bufferIdx, size_t width, size_t height) override; + void submitTexels(int textureIdx, size_t updateWidthInTexels, size_t updateHeight) override; private: const GLenum m_activeTextureIdx;
diff --git a/renderer/gl/gl_utils.cpp b/renderer/gl/gl_utils.cpp index baa611c..75e8a11 100644 --- a/renderer/gl/gl_utils.cpp +++ b/renderer/gl/gl_utils.cpp
@@ -15,9 +15,10 @@ void CompileAndAttachShader(GLuint program, GLuint type, const char* source, + const GLExtensions& extensions, const char* versionString) { - CompileAndAttachShader(program, type, nullptr, 0, &source, 1, versionString); + CompileAndAttachShader(program, type, nullptr, 0, &source, 1, extensions, versionString); } void CompileAndAttachShader(GLuint program, @@ -26,17 +27,26 @@ size_t numDefines, const char* inputSources[], size_t numInputSources, + const GLExtensions& extensions, const char* versionString) { - GLuint shader = - CompileShader(type, defines, numDefines, inputSources, numInputSources, versionString); + GLuint shader = CompileShader(type, + defines, + numDefines, + inputSources, + numInputSources, + extensions, + versionString); glAttachShader(program, shader); glDeleteShader(shader); } -GLuint CompileShader(GLuint type, const char* source, const char* versionString) +GLuint CompileShader(GLuint type, + const char* source, + const GLExtensions& extensions, + const char* versionString) { - return CompileShader(type, nullptr, 0, &source, 1, versionString); + return CompileShader(type, nullptr, 0, &source, 1, extensions, versionString); } GLuint CompileShader(GLuint type, @@ -44,6 +54,7 @@ size_t numDefines, const char* inputSources[], size_t numInputSources, + const GLExtensions& extensions, const char* versionString) { std::vector<const char*> sources; @@ -56,6 +67,10 @@ { sources.push_back("#define " GLSL_FRAGMENT "\n"); } + if (type == GL_VERTEX_SHADER && !extensions.ANGLE_base_vertex_base_instance_shader_builtin) + { + sources.push_back("#define " GLSL_ENABLE_BASE_INSTANCE_POLYFILL "\n"); + } std::ostringstream definesStream; for (size_t i = 0; i < numDefines; ++i) {
diff --git a/renderer/gl/gl_utils.hpp b/renderer/gl/gl_utils.hpp index 5cebfa1..558fa8c 100644 --- a/renderer/gl/gl_utils.hpp +++ b/renderer/gl/gl_utils.hpp
@@ -13,6 +13,7 @@ void CompileAndAttachShader(GLuint program, GLuint type, const char* source, + const GLExtensions&, const char* versionString = nullptr); void CompileAndAttachShader(GLuint program, @@ -21,10 +22,12 @@ size_t numDefines, const char* sources[], size_t numSources, + const GLExtensions&, const char* versionString = nullptr); [[nodiscard]] GLuint CompileShader(GLuint type, const char* source, + const GLExtensions&, const char* versionString = nullptr); [[nodiscard]] GLuint CompileShader(GLuint type, @@ -32,6 +35,7 @@ size_t numDefines, const char* sources[], size_t numSources, + const GLExtensions&, const char* versionString = nullptr); void LinkProgram(GLuint program);
diff --git a/renderer/gl/pls_impl_ext_native.cpp b/renderer/gl/pls_impl_ext_native.cpp index 6bb75a7..94c11d4 100644 --- a/renderer/gl/pls_impl_ext_native.cpp +++ b/renderer/gl/pls_impl_ext_native.cpp
@@ -33,7 +33,7 @@ PLSLoadStoreProgram(const PLSLoadStoreProgram&) = delete; PLSLoadStoreProgram& operator=(const PLSLoadStoreProgram&) = delete; - PLSLoadStoreProgram(uint32_t ops, GLuint vertexShader) + PLSLoadStoreProgram(uint32_t ops, GLuint vertexShader, const GLExtensions& extensions) { std::vector<const char*> defines{GLSL_PLS_IMPL_EXT_NATIVE}; if (ops & loadstoreops::kClearColor) @@ -66,7 +66,8 @@ defines.data(), defines.size(), &source, - 1); + 1, + extensions); glutils::LinkProgram(m_id); if (ops & loadstoreops::kClearColor) @@ -88,12 +89,12 @@ class PLSRenderContextGL::PLSImplEXTNative : public PLSRenderContextGL::PLSImpl { public: - PLSImplEXTNative() + PLSImplEXTNative(const GLExtensions& extensions) : m_extensions(extensions) { // We have to manually implement load/store operations from a shader when using // EXT_shader_pixel_local_storage. m_plsLoadStoreVertexShader = - glutils::CompileShader(GL_VERTEX_SHADER, glsl::pls_load_store_ext); + glutils::CompileShader(GL_VERTEX_SHADER, glsl::pls_load_store_ext, extensions); glGenVertexArrays(1, &m_plsLoadStoreVAO); } @@ -157,7 +158,8 @@ { // Otherwise we have to initialize pixel local storage with a fullscreen draw. const PLSLoadStoreProgram& plsProgram = - m_plsLoadStorePrograms.try_emplace(ops, ops, m_plsLoadStoreVertexShader) + m_plsLoadStorePrograms + .try_emplace(ops, ops, m_plsLoadStoreVertexShader, m_extensions) .first->second; context->bindProgram(plsProgram.id()); if (plsProgram.clearColorUniLocation() >= 0) @@ -175,7 +177,8 @@ // the main framebuffer. uint32_t ops = loadstoreops::kStoreColor; const PLSLoadStoreProgram& plsProgram = - m_plsLoadStorePrograms.try_emplace(ops, ops, m_plsLoadStoreVertexShader).first->second; + m_plsLoadStorePrograms.try_emplace(ops, ops, m_plsLoadStoreVertexShader, m_extensions) + .first->second; context->bindProgram(plsProgram.id()); context->bindVAO(m_plsLoadStoreVAO); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -186,13 +189,15 @@ const char* shaderDefineName() const override { return GLSL_PLS_IMPL_EXT_NATIVE; } private: + const GLExtensions m_extensions; std::map<uint32_t, PLSLoadStoreProgram> m_plsLoadStorePrograms; // Keyed by loadstoreops. GLuint m_plsLoadStoreVertexShader = 0; GLuint m_plsLoadStoreVAO = 0; }; -std::unique_ptr<PLSRenderContextGL::PLSImpl> PLSRenderContextGL::MakePLSImplEXTNative() +std::unique_ptr<PLSRenderContextGL::PLSImpl> PLSRenderContextGL::MakePLSImplEXTNative( + const GLExtensions& extensions) { - return std::make_unique<PLSImplEXTNative>(); + return std::make_unique<PLSImplEXTNative>(extensions); } } // namespace rive::pls
diff --git a/renderer/gl/pls_impl_framebuffer_fetch.cpp b/renderer/gl/pls_impl_framebuffer_fetch.cpp index 6796b4d..7454cd6 100644 --- a/renderer/gl/pls_impl_framebuffer_fetch.cpp +++ b/renderer/gl/pls_impl_framebuffer_fetch.cpp
@@ -18,7 +18,7 @@ class PLSRenderContextGL::PLSImplFramebufferFetch : public PLSRenderContextGL::PLSImpl { public: - PLSImplFramebufferFetch(GLExtensions extensions) : m_extensions(extensions) {} + PLSImplFramebufferFetch(const GLExtensions& extensions) : m_extensions(extensions) {} rcp<PLSRenderTargetGL> wrapGLRenderTarget(GLuint framebufferID, size_t width, @@ -125,7 +125,7 @@ }; std::unique_ptr<PLSRenderContextGL::PLSImpl> PLSRenderContextGL::MakePLSImplFramebufferFetch( - GLExtensions extensions) + const GLExtensions& extensions) { return std::make_unique<PLSImplFramebufferFetch>(extensions); }
diff --git a/renderer/gl/pls_render_context_gl.cpp b/renderer/gl/pls_render_context_gl.cpp index 0562f24..1365c57 100644 --- a/renderer/gl/pls_render_context_gl.cpp +++ b/renderer/gl/pls_render_context_gl.cpp
@@ -59,6 +59,7 @@ 0, colorRampSources, 2, + m_extensions, m_shaderVersionString); glutils::CompileAndAttachShader(m_colorRampProgram, GL_FRAGMENT_SHADER, @@ -66,6 +67,7 @@ 0, colorRampSources, 2, + m_extensions, m_shaderVersionString); glutils::LinkProgram(m_colorRampProgram); glUniformBlockBinding(m_colorRampProgram, @@ -87,6 +89,7 @@ 0, tessellateSources, 2, + m_extensions, m_shaderVersionString); glutils::CompileAndAttachShader(m_tessellateProgram, GL_FRAGMENT_SHADER, @@ -94,6 +97,7 @@ 0, tessellateSources, 2, + m_extensions, m_shaderVersionString); glutils::LinkProgram(m_tessellateProgram); bindProgram(m_tessellateProgram); @@ -133,8 +137,6 @@ glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, nullptr); - glVertexAttribDivisor(3, 1); - glGenVertexArrays(1, &m_interiorTrianglesVAO); bindVAO(m_interiorTrianglesVAO); glEnableVertexAttribArray(0); @@ -182,6 +184,7 @@ std::unique_ptr<TexelBufferRing> PLSRenderContextGL::makeTexelBufferRing( TexelBufferRing::Format format, + Renderable, size_t widthInItems, size_t height, size_t texelsPerItem, @@ -262,11 +265,6 @@ { defines.push_back(GLSL_ENABLE_HSL_BLEND_MODES); } - if (shaderType == GL_VERTEX_SHADER && - !context->m_extensions.ANGLE_base_vertex_base_instance_shader_builtin) - { - defines.push_back(GLSL_BASE_INSTANCE_POLYFILL); - } std::vector<const char*> sources; sources.push_back(glsl::common); @@ -291,6 +289,7 @@ defines.size(), sources.data(), sources.size(), + context->m_extensions, context->m_shaderVersionString); } @@ -459,7 +458,8 @@ m_plsImpl->ensureRasterOrderingEnabled(true); bindVAO(m_drawVAO); uint32_t indexCount = PatchIndexCount(drawType); - void* indexOffset = reinterpret_cast<void*>(PatchIndexOffset(drawType)); + uint32_t baseIndex = PatchBaseIndex(drawType); + void* indexOffset = reinterpret_cast<void*>(baseIndex * sizeof(uint16_t)); if (m_extensions.ANGLE_base_vertex_base_instance_shader_builtin) { glDrawElementsInstancedBaseInstanceEXT(GL_TRIANGLES, @@ -610,6 +610,12 @@ // varyings. platformFeatures.avoidFlatVaryings = true; } + if (strstr(rendererString, "Direct3D")) + { + // This extension is polyfilled on D3D anyway. Just don't use it so we can make sure to test + // our own fallback. + extensions.ANGLE_base_vertex_base_instance_shader_builtin = false; + } #ifdef RIVE_GLES loadGLESExtensions(extensions); // Android doesn't load extension functions for us. @@ -617,7 +623,7 @@ (extensions.ARM_shader_framebuffer_fetch || extensions.EXT_shader_framebuffer_fetch)) { return std::unique_ptr<PLSRenderContextGL>( - new PLSRenderContextGL(platformFeatures, extensions, MakePLSImplEXTNative())); + new PLSRenderContextGL(platformFeatures, extensions, MakePLSImplEXTNative(extensions))); } if (extensions.EXT_shader_framebuffer_fetch)
diff --git a/renderer/metal/buffer_ring_metal.h b/renderer/metal/buffer_ring_metal.h index ac51158..8024f6e 100644 --- a/renderer/metal/buffer_ring_metal.h +++ b/renderer/metal/buffer_ring_metal.h
@@ -42,7 +42,7 @@ id<MTLTexture> submittedTexture() const { return m_textures[submittedBufferIdx()]; } protected: - void submitTexels(int bufferIdx, size_t width, size_t height) override; + void submitTexels(int textureIdx, size_t updateWidthInTexels, size_t updateHeight) override; private: id<MTLTexture> m_textures[kBufferRingSize];
diff --git a/renderer/metal/pls_render_context_metal.mm b/renderer/metal/pls_render_context_metal.mm index adccae0..cf48520 100644 --- a/renderer/metal/pls_render_context_metal.mm +++ b/renderer/metal/pls_render_context_metal.mm
@@ -245,6 +245,7 @@ std::unique_ptr<TexelBufferRing> PLSRenderContextMetal::makeTexelBufferRing( TexelBufferRing::Format format, + Renderable, size_t widthInItems, size_t height, size_t texelsPerItem, @@ -448,7 +449,7 @@ indexCount:PatchIndexCount(drawType) indexType:MTLIndexTypeUInt16 indexBuffer:m_pathPatchIndexBuffer - indexBufferOffset:PatchIndexOffset(drawType) + indexBufferOffset:PatchBaseIndex(drawType) * sizeof(uint16_t) instanceCount:draw->vertexOrInstanceCount baseVertex:0 baseInstance:draw->baseVertexOrInstance];
diff --git a/renderer/pls_render_context.cpp b/renderer/pls_render_context.cpp index 37011fc..8d79dd7 100644 --- a/renderer/pls_render_context.cpp +++ b/renderer/pls_render_context.cpp
@@ -223,6 +223,7 @@ { assert(!m_pathBuffer.mapped()); m_pathBuffer.reset(makeTexelBufferRing(TexelBufferRing::Format::rgba32ui, + Renderable::no, kPathTextureWidthInItems, targetPathTextureHeight, kPathTexelsPerItem, @@ -250,6 +251,7 @@ { assert(!m_contourBuffer.mapped()); m_contourBuffer.reset(makeTexelBufferRing(TexelBufferRing::Format::rgba32ui, + Renderable::no, kContourTextureWidthInItems, targetContourTextureHeight, kContourTexelsPerItem, @@ -285,6 +287,7 @@ { assert(!m_gradTexelBuffer.mapped()); m_gradTexelBuffer.reset(makeTexelBufferRing(TexelBufferRing::Format::rgba8, + Renderable::yes, kGradTextureWidthInSimpleRamps, targetGradTextureHeight, 2, // 2 texels per simple ramp.
diff --git a/renderer/shaders/advanced_blend.glsl b/renderer/shaders/advanced_blend.glsl index 57950e3..791c90d 100644 --- a/renderer/shaders/advanced_blend.glsl +++ b/renderer/shaders/advanced_blend.glsl
@@ -104,7 +104,7 @@ half lbase = lumv3(cbase); half llum = lumv3(clum); half ldiff = llum - lbase; - half3 color = cbase + make_half3(ldiff); + half3 color = cbase + make_half3(ldiff, ldiff, ldiff); return clip_color(color); } @@ -125,7 +125,7 @@ } else { - color = make_half3(0); + color = make_half3(0, 0, 0); } return set_lum(color, clum); } @@ -139,7 +139,7 @@ { // The function f() operates on un-multiplied rgb values and dictates the look of the advanced // blend equations. - half3 f; + half3 f = make_half3(0, 0, 0); switch (mode) { case BLEND_MODE_MULTIPLY: @@ -149,7 +149,8 @@ f = src.rgb + dst.rgb - src.rgb * dst.rgb; break; case BLEND_MODE_OVERLAY: - for (ushort i = 0u; i < 3u; ++i) + { + for (int i = 0; i < 3; ++i) { if (dst[i] <= .5) f[i] = 2. * src[i] * dst[i]; @@ -157,6 +158,7 @@ f[i] = 1. - 2. * (1. - src[i]) * (1. - dst[i]); } break; + } case BLEND_MODE_DARKEN: f = min(src.rgb, dst.rgb); break; @@ -166,19 +168,20 @@ case BLEND_MODE_COLORDODGE: // ES3 spec, 4.5.1 Range and Precision: dividing a non-zero by 0 results in the // appropriately signed IEEE Inf. - f = mix(min(dst.rgb / (1. - src.rgb), make_half3(1)), - make_half3(0), - lessThanEqual(dst.rgb, make_half3(0))); + f = mix(min(dst.rgb / (1. - src.rgb), make_half3(1, 1, 1)), + make_half3(0, 0, 0), + lessThanEqual(dst.rgb, make_half3(0, 0, 0))); break; case BLEND_MODE_COLORBURN: // ES3 spec, 4.5.1 Range and Precision: dividing a non-zero by 0 results in the // appropriately signed IEEE Inf. f = mix(1. - min((1. - dst.rgb) / src.rgb, 1.), - make_half3(1), - greaterThanEqual(dst.rgb, make_half3(1))); + make_half3(1, 1, 1), + greaterThanEqual(dst.rgb, make_half3(1, 1, 1))); break; case BLEND_MODE_HARDLIGHT: - for (ushort i = 0u; i < 3u; ++i) + { + for (int i = 0; i < 3; ++i) { if (src[i] <= .5) f[i] = 2. * src[i] * dst[i]; @@ -186,8 +189,10 @@ f[i] = 1. - 2. * (1. - src[i]) * (1. - dst[i]); } break; + } case BLEND_MODE_SOFTLIGHT: - for (ushort i = 0u; i < 3u; ++i) + { + for (int i = 0; i < 3; ++i) { if (src[i] <= 0.5) f[i] = dst[i] - (1. - 2. * src[i]) * dst[i] * (1. - dst[i]); @@ -198,6 +203,7 @@ f[i] = dst[i] + (2. * src[i] - 1.) * (sqrt(dst[i]) - dst[i]); } break; + } case BLEND_MODE_DIFFERENCE: f = abs(dst.rgb - src.rgb); break; @@ -208,19 +214,19 @@ // The HSL blend equations are only well defined when the values of the input color // components are in the range [0..1]. case BLEND_MODE_HUE: - src.rgb = clamp(src.rgb, make_half3(0), make_half3(1)); + src.rgb = clamp(src.rgb, make_half3(0, 0, 0), make_half3(1, 1, 1)); f = set_lum_sat(src.rgb, dst.rgb, dst.rgb); break; case BLEND_MODE_SATURATION: - src.rgb = clamp(src.rgb, make_half3(0), make_half3(1)); + src.rgb = clamp(src.rgb, make_half3(0, 0, 0), make_half3(1, 1, 1)); f = set_lum_sat(dst.rgb, src.rgb, dst.rgb); break; case BLEND_MODE_COLOR: - src.rgb = clamp(src.rgb, make_half3(0), make_half3(1)); + src.rgb = clamp(src.rgb, make_half3(0, 0, 0), make_half3(1, 1, 1)); f = set_lum(src.rgb, dst.rgb); break; case BLEND_MODE_LUMINOSITY: - src.rgb = clamp(src.rgb, make_half3(0), make_half3(1)); + src.rgb = clamp(src.rgb, make_half3(0, 0, 0), make_half3(1, 1, 1)); f = set_lum(dst.rgb, src.rgb); break; #endif @@ -243,6 +249,6 @@ // A = X*p0(As,Ad) + Y*p1(As,Ad) + Z*p2(As,Ad) // // NOTE: (X,Y,Z) always == (1,1,1), so it is ignored in this implementation. - return make_half3x4(f, 1, src.rgb, 1, dst.rgb, 1) * p; + return MUL(make_half3x4(f, 1, src.rgb, 1, dst.rgb, 1), p); } #endif
diff --git a/renderer/shaders/color_ramp.glsl b/renderer/shaders/color_ramp.glsl index 383161c..0d26ebf 100644 --- a/renderer/shaders/color_ramp.glsl +++ b/renderer/shaders/color_ramp.glsl
@@ -5,53 +5,58 @@ // This shader draws horizontal color ramps into a gradient texture, which will later be sampled by // the renderer for drawing gradients. -VARYING_BLOCK_BEGIN(Varyings) -NO_PERSPECTIVE VARYING half4 rampColor; -VARYING_BLOCK_END - #ifdef @VERTEX ATTR_BLOCK_BEGIN(Attrs) -ATTR(0) uint4 span; // [horizontalSpan, y, color0, color1] +ATTR(0, uint4, @a_span); // [spanX, y, color0, color1] ATTR_BLOCK_END +#endif + +VARYING_BLOCK_BEGIN(Varyings) +NO_PERSPECTIVE VARYING(half4, v_rampColor); +VARYING_BLOCK_END(_pos) + +#ifdef @VERTEX +VERTEX_TEXTURE_BLOCK_BEGIN(VertexTextures) +VERTEX_TEXTURE_BLOCK_END half4 unpackColorInt(uint color) { - return make_half4((uint4(color) >> uint4(16, 8, 0, 24)) & 0xffu) / 255.; + return make_half4((uint4(color, color, color, color) >> uint4(16, 8, 0, 24)) & 0xffu) / 255.; } -VERTEX_MAIN( -#ifdef METAL - @colorRampVertexMain, - Varyings, - varyings, - uint VERTEX_ID [[vertex_id]], - uint INSTANCE_ID [[instance_id]], - constant @Uniforms& uniforms [[buffer(0)]], - constant Attrs* attrs [[buffer(1)]] -#endif -) +VERTEX_MAIN(@colorRampVertexMain, + @Uniforms, + uniforms, + Attrs, + attrs, + Varyings, + varyings, + VertexTextures, + textures, + _vertexID, + _instanceID, + _pos) { - ATTR_LOAD(uint4, attrs, span, INSTANCE_ID); - uint horizontalSpan = span.x; - float2 coord = float2( - float((VERTEX_ID & 1) == 0 ? horizontalSpan & 0xffffu : horizontalSpan >> 16) / 65536., - float(span.y) + ((VERTEX_ID & 2) == 0 ? .0 : 1.)); - FLD(varyings, rampColor) = unpackColorInt((VERTEX_ID & 1) == 0 ? span.z : span.w); - POSITION.xy = coord * float2(2, uniforms.gradInverseViewportY) - 1.; - POSITION.zw = float2(0, 1); - EMIT_OFFSCREEN_VERTEX(varyings); + ATTR_UNPACK(_instanceID, attrs, @a_span, uint4); + + VARYING_INIT(varyings, v_rampColor, half4); + + float x = float((_vertexID & 1) == 0 ? @a_span.x & 0xffffu : @a_span.x >> 16) / 65536.; + float y = float(@a_span.y) + ((_vertexID & 2) == 0 ? .0 : 1.); + v_rampColor = unpackColorInt((_vertexID & 1) == 0 ? @a_span.z : @a_span.w); + _pos.x = x * 2. - 1.; + _pos.y = y * uniforms.gradInverseViewportY - 1.; + _pos.zw = float2(0, 1); + + VARYING_PACK(varyings, v_rampColor); + EMIT_OFFSCREEN_VERTEX(varyings, _pos); } #endif #ifdef @FRAGMENT -FRAG_DATA_TYPE(half4) -FRAG_DATA_MAIN( -#ifdef METAL - @colorRampFragmentMain, - Varyings varyings [[stage_in]] -#endif -) +FRAG_DATA_MAIN(half4, @colorRampFragmentMain, Varyings, varyings) { - EMIT_FRAG_DATA(FLD(varyings, rampColor)); + VARYING_UNPACK(varyings, v_rampColor, half4); + EMIT_FRAG_DATA(v_rampColor); } #endif
diff --git a/renderer/shaders/common.glsl b/renderer/shaders/common.glsl index 77b310d..20f1fe6 100644 --- a/renderer/shaders/common.glsl +++ b/renderer/shaders/common.glsl
@@ -71,6 +71,8 @@ return color; } +INLINE float2x2 make_float2x2(float4 f) { return float2x2(f.xy, f.zw); } + #ifdef @VERTEX UNIFORM_BLOCK_BEGIN(@Uniforms) float gradInverseViewportY;
diff --git a/renderer/shaders/draw.glsl b/renderer/shaders/draw.glsl index b742b84..026be4c 100644 --- a/renderer/shaders/draw.glsl +++ b/renderer/shaders/draw.glsl
@@ -8,42 +8,38 @@ #define FAN_VERTEX 1 #define FAN_MIDPOINT_VERTEX 2 -VARYING_BLOCK_BEGIN(Varyings) -NO_PERSPECTIVE VARYING float4 varying_paint; -#ifdef @DRAW_INTERIOR_TRIANGLES -@OPTIONALLY_FLAT VARYING half windingWeight; -#else -NO_PERSPECTIVE VARYING half2 edgeDistance; -#endif -@OPTIONALLY_FLAT VARYING half pathID; -#ifdef @ENABLE_PATH_CLIPPING -@OPTIONALLY_FLAT VARYING half clipID; -#endif -#ifdef @ENABLE_ADVANCED_BLEND -@OPTIONALLY_FLAT VARYING half blendMode; -#endif -VARYING_BLOCK_END - #ifdef @VERTEX - -// Only used by GL platforms that don't support EXT_base_instance. -#ifdef @BASE_INSTANCE_POLYFILL -uniform int @baseInstancePolyfill; -#endif - -VERTEX_TEXTURE_BLOCK_BEGIN(VertexTextures) -TEXTURE_RGBA32UI(0) @tessVertexTexture; -TEXTURE_RGBA32UI(1) @pathTexture; -TEXTURE_RGBA32UI(2) @contourTexture; -VERTEX_TEXTURE_BLOCK_END - ATTR_BLOCK_BEGIN(Attrs) #ifdef @DRAW_INTERIOR_TRIANGLES -ATTR(0) packed_float3 triangleVertex; +ATTR(0, packed_float3, @a_triangleVertex); #else -ATTR(0) float4 patchVertexData; // [localVertexID, outset, fillCoverage, vertexType] +ATTR(0, float4, @a_patchVertexData); // [localVertexID, outset, fillCoverage, vertexType] #endif ATTR_BLOCK_END +#endif + +VARYING_BLOCK_BEGIN(Varyings) +NO_PERSPECTIVE VARYING(float4, v_paint); +#ifdef @DRAW_INTERIOR_TRIANGLES +@OPTIONALLY_FLAT VARYING(half, v_windingWeight); +#else +NO_PERSPECTIVE VARYING(half2, v_edgeDistance); +#endif +@OPTIONALLY_FLAT VARYING(half, v_pathID); +#ifdef @ENABLE_PATH_CLIPPING +@OPTIONALLY_FLAT VARYING(half, v_clipID); +#endif +#ifdef @ENABLE_ADVANCED_BLEND +@OPTIONALLY_FLAT VARYING(half, v_blendMode); +#endif +VARYING_BLOCK_END(_pos) + +#ifdef @VERTEX +VERTEX_TEXTURE_BLOCK_BEGIN(VertexTextures) +TEXTURE_RGBA32UI(0, @tessVertexTexture); +TEXTURE_RGBA32UI(1, @pathTexture); +TEXTURE_RGBA32UI(2, @contourTexture); +VERTEX_TEXTURE_BLOCK_END int2 tessTexelCoord(int texelIndex) { @@ -51,47 +47,70 @@ texelIndex >> TESS_TEXTURE_WIDTH_LOG2); } -float calc_aa_radius(float2x2 matrix, float2 normalized) +float calc_aa_radius(float2x2 mat, float2 normalized) { - float2 v = matrix * normalized; + float2 v = MUL(mat, normalized); return (abs(v.x) + abs(v.y)) * (1. / dot(v, v)) * AA_RADIUS; } -VERTEX_MAIN( -#ifdef METAL - @drawVertexMain, - Varyings, - varyings, - uint VERTEX_ID [[vertex_id]], - uint INSTANCE_ID [[instance_id]], - uint BASE_INSTANCE [[base_instance]], - constant @Uniforms& uniforms [[buffer(0)]], - constant Attrs* attrs [[buffer(1)]], - VertexTextures textures +#ifdef @ENABLE_BASE_INSTANCE_POLYFILL +// Define a uniform that will supply the base instance if we're on a platform that doesn't provide +// this as a built-in. +BASE_INSTANCE_POLYFILL_DECL(@baseInstancePolyfill); #endif -) + +VERTEX_MAIN(@drawVertexMain, + @Uniforms, + uniforms, + Attrs, + attrs, + Varyings, + varyings, + VertexTextures, + textures, + _vertexID, + _instanceID, + _pos) { +#ifdef @DRAW_INTERIOR_TRIANGLES + ATTR_UNPACK(_vertexID, attrs, @a_triangleVertex, float3); +#else + ATTR_UNPACK(_vertexID, attrs, @a_patchVertexData, float4); +#endif + + VARYING_INIT(varyings, v_paint, float4); +#ifdef @DRAW_INTERIOR_TRIANGLES + VARYING_INIT(varyings, v_windingWeight, half); +#else + VARYING_INIT(varyings, v_edgeDistance, half2); +#endif + VARYING_INIT(varyings, v_pathID, half); +#ifdef @ENABLE_PATH_CLIPPING + VARYING_INIT(varyings, v_clipID, half); +#endif +#ifdef @ENABLE_ADVANCED_BLEND + VARYING_INIT(varyings, v_blendMode, half); +#endif + bool shouldDiscardVertex = false; #ifdef @DRAW_INTERIOR_TRIANGLES - ATTR_LOAD(float3, attrs, triangleVertex, VERTEX_ID); - uint pathIDBits = floatBitsToUint(triangleVertex.z) & 0xffffu; + uint pathIDBits = floatBitsToUint(@a_triangleVertex.z) & 0xffffu; #else // Unpack patchVertexData. - ATTR_LOAD(float4, attrs, patchVertexData, VERTEX_ID); - int localVertexID = int(patchVertexData.x); - float outset = patchVertexData.y; - float fillCoverage = patchVertexData.z; - int patchSegmentSpan = floatBitsToInt(patchVertexData.w) >> 2; - int vertexType = floatBitsToInt(patchVertexData.w) & 3; + int localVertexID = int(@a_patchVertexData.x); + float outset = @a_patchVertexData.y; + float fillCoverage = @a_patchVertexData.z; + int patchSegmentSpan = floatBitsToInt(@a_patchVertexData.w) >> 2; + int vertexType = floatBitsToInt(@a_patchVertexData.w) & 3; // Fetch the tessellation vertex we belong to. - int vertexIdx = INSTANCE_ID * patchSegmentSpan + localVertexID; -#ifdef @BASE_INSTANCE_POLYFILL - vertexIdx += baseInstancePolyfill * patchSegmentSpan; + int tessVertexIdx = _instanceID * patchSegmentSpan + localVertexID; +#ifdef @ENABLE_BASE_INSTANCE_POLYFILL + tessVertexIdx += @baseInstancePolyfill * patchSegmentSpan; #endif - int2 tessVertexTexelCoord = tessTexelCoord(vertexIdx); - uint4 tessVertexData = TEXEL_FETCH(textures, @tessVertexTexture, tessVertexTexelCoord, 0); + int2 tessVertexTexelCoord = tessTexelCoord(tessVertexIdx); + uint4 tessVertexData = TEXEL_FETCH(textures, @tessVertexTexture, tessVertexTexelCoord); uint contourIDWithFlags = tessVertexData.w; bool isClosingVertexOfContour = localVertexID == patchSegmentSpan && (contourIDWithFlags & FIRST_VERTEX_OF_CONTOUR_FLAG) != 0u; @@ -99,14 +118,14 @@ { // The right vertex crossed over into a new contour. Fetch the previous vertex, which will // be the final vertex of the contour we're trying to draw. - tessVertexTexelCoord = tessTexelCoord(vertexIdx - 1); - tessVertexData = TEXEL_FETCH(textures, @tessVertexTexture, tessVertexTexelCoord, 0); + tessVertexTexelCoord = tessTexelCoord(tessVertexIdx - 1); + tessVertexData = TEXEL_FETCH(textures, @tessVertexTexture, tessVertexTexelCoord); contourIDWithFlags = tessVertexData.w; } // Fetch and unpack the contour referenced by the tessellation vertex. uint4 contourData = - TEXEL_FETCH(textures, @contourTexture, contour_texel_coord(contourIDWithFlags), 0); + TEXEL_FETCH(textures, @contourTexture, contour_texel_coord(contourIDWithFlags)); float2 midpoint = uintBitsToFloat(contourData.xy); uint pathIDBits = contourData.z; uint vertexIndex0 = contourData.w; @@ -114,19 +133,18 @@ // Fetch and unpack the path. int2 pathTexelCoord = path_texel_coord(pathIDBits); - float2x2 matrix = - make_float2x2(uintBitsToFloat(TEXEL_FETCH(textures, @pathTexture, pathTexelCoord, 0))); - uint4 pathData = TEXEL_FETCH(textures, @pathTexture, pathTexelCoord + int2(1, 0), 0); + float2x2 mat = + make_float2x2(uintBitsToFloat(TEXEL_FETCH(textures, @pathTexture, pathTexelCoord))); + uint4 pathData = TEXEL_FETCH(textures, @pathTexture, pathTexelCoord + int2(1, 0)); float2 translate = uintBitsToFloat(pathData.xy); uint pathParams = pathData.w; #ifdef @DRAW_INTERIOR_TRIANGLES // The vertex position is encoded directly in vertex data when drawing triangles. - float2 vertexPosition = matrix * triangleVertex.xy + translate; + float2 vertexPosition = MUL(mat, @a_triangleVertex.xy) + translate; // When we belong to a non-overlapping interior triangulation, the winding sign and weight are // also encoded directly in vertex data. - FLD(varyings, windingWeight) = - float(floatBitsToInt(triangleVertex.z) >> 16) * sign(determinant(matrix)); + v_windingWeight = float(floatBitsToInt(@a_triangleVertex.z) >> 16) * sign(determinant(mat)); #else float strokeRadius = uintBitsToFloat(pathData.z); @@ -140,7 +158,7 @@ // The contour is closed and we are the closing vertex. Wrap back around full circle and // re-emit the first tessellation vertex. int2 vertexTexelCoord0 = tessTexelCoord(int(vertexIndex0)); - tessVertexData = TEXEL_FETCH(textures, @tessVertexTexture, vertexTexelCoord0, 0); + tessVertexData = TEXEL_FETCH(textures, @tessVertexTexture, vertexTexelCoord0); contourIDWithFlags = tessVertexData.w; } } @@ -157,24 +175,23 @@ if ((contourIDWithFlags & RIGHT_JOIN_FLAG) != 0u) outset = max(outset, .0); - float aaRadius = calc_aa_radius(matrix, norm); - float globalCoverage = 1.; + float aaRadius = calc_aa_radius(mat, norm); + half globalCoverage = 1.; if (aaRadius > strokeRadius) { - // The stroke is narrower than the AA ramp. Instead of emitting subpixel geometry, - // make the stroke as wide as the AA ramp and apply a global coverage multiplier. - globalCoverage = strokeRadius / aaRadius; + // The stroke is narrower than the AA ramp. Instead of emitting subpixel geometry, make + // the stroke as wide as the AA ramp and apply a global coverage multiplier. + globalCoverage = make_half(strokeRadius) / make_half(aaRadius); strokeRadius = aaRadius; } // Extend the vertex by half the width of the AA ramp. - float2 vertexOffset = norm * (strokeRadius + aaRadius); // Bloat stroke width for AA. + float2 vertexOffset = MUL(norm, strokeRadius + aaRadius); // Bloat stroke width for AA. // Calculate the AA distance to both the outset and inset edges of the stroke. The fragment // shader will use whichever is lesser. float x = outset * (strokeRadius + aaRadius); - FLD(varyings, edgeDistance) = - make_half2((1. / (aaRadius * 2.)) * (float2(x, -x) + strokeRadius) + .5); + v_edgeDistance = make_half2((1. / (aaRadius * 2.)) * (float2(x, -x) + strokeRadius) + .5); uint joinType = contourIDWithFlags & JOIN_TYPE_MASK; if (joinType != 0u) @@ -183,8 +200,8 @@ // the same as the miter line. The first two vertices in the join peek forward to figure // out the bisector, and the final two peek backward. int peekDir = (contourIDWithFlags & JOIN_TANGENT_0_FLAG) != 0u ? 2 : -2; - int2 otherJoinTexelCoord = tessTexelCoord(vertexIdx + peekDir); - uint4 otherJoinData = TEXEL_FETCH(textures, @tessVertexTexture, otherJoinTexelCoord, 0); + int2 otherJoinTexelCoord = tessTexelCoord(tessVertexIdx + peekDir); + uint4 otherJoinData = TEXEL_FETCH(textures, @tessVertexTexture, otherJoinTexelCoord); float otherJoinTheta = uintBitsToFloat(otherJoinData.z); float joinAngle = abs(otherJoinTheta - theta); if (joinAngle > PI) @@ -193,7 +210,7 @@ bool isLeftJoin = (contourIDWithFlags & LEFT_JOIN_FLAG) != 0u; float bisectTheta = joinAngle * (isTan0 == isLeftJoin ? -.5 : .5) + theta; float2 bisector = float2(sin(bisectTheta), -cos(bisectTheta)); - float bisectAARadius = calc_aa_radius(matrix, bisector); + float bisectAARadius = calc_aa_radius(mat, bisector); // Generalize everything to a "miter-clip", which is proposed in the SVG-2 draft. Bevel // joins are converted to miter-clip joins with a miter limit of 1/2 pixel. They @@ -244,7 +261,7 @@ float2 bisectAAOffset = bisector * clipAARadius; float2 k = float2(dot(vertexOffset, vertexOffset), dot(bisectAAOffset, bisectAAOffset)); - vertexOffset = k * inverse(float2x2(vertexOffset, bisectAAOffset)); + vertexOffset = MUL(k, inverse(float2x2(vertexOffset, bisectAAOffset))); } } // The clip distance tells us how to antialias the outer clipped edge. Since joins only @@ -253,15 +270,15 @@ float2 pt = abs(outset) * vertexOffset; float clipDistance = (clipAARadius - dot(pt, bisector)) / (bisectAARadius * 2.); if ((contourIDWithFlags & LEFT_JOIN_FLAG) != 0u) - FLD(varyings, edgeDistance).y = clipDistance; + v_edgeDistance.y = make_half(clipDistance); else - FLD(varyings, edgeDistance).x = clipDistance; + v_edgeDistance.x = make_half(clipDistance); } // Strokes identify themselves by emitting a negative edgeDistance. - FLD(varyings, edgeDistance) *= -globalCoverage; + v_edgeDistance *= -globalCoverage; - postTransformVertexOffset = matrix * (outset * vertexOffset); + postTransformVertexOffset = MUL(mat, outset * vertexOffset); // Throw away the fan triangles since we're a stroke. if (vertexType != STROKE_VERTEX) @@ -274,43 +291,41 @@ origin = midpoint; // Offset the vertex for Manhattan AA. - postTransformVertexOffset = sign(matrix * (outset * norm)) * AA_RADIUS; - FLD(varyings, edgeDistance) = make_half2(fillCoverage, 1); + postTransformVertexOffset = sign(MUL(mat, outset * norm)) * AA_RADIUS; + v_edgeDistance = make_half2(fillCoverage, 1); // If we're actually just drawing a triangle, throw away the entire patch except a single // fan triangle. if ((contourIDWithFlags & RETROFITTED_TRIANGLE_FLAG) != 0u && vertexType != FAN_VERTEX) shouldDiscardVertex = true; } - float2 vertexPosition = matrix * origin + postTransformVertexOffset + translate; + float2 vertexPosition = MUL(mat, origin) + postTransformVertexOffset + translate; #endif // Encode the integral pathID as a "half" that we know the hardware will see as a unique value // in the fragment shader. - FLD(varyings, pathID) = - unpackHalf2x16((pathIDBits + MAX_DENORM_F16) * uniforms.pathIDGranularity).r; + v_pathID = unpackHalf2x16((pathIDBits + MAX_DENORM_F16) * uniforms.pathIDGranularity).r; // Indicate even-odd fill rule by making pathID negative. if ((pathParams & EVEN_ODD_FLAG) != 0u) - FLD(varyings, pathID) = -FLD(varyings, pathID); + v_pathID = -v_pathID; uint paintType = (pathParams >> 20) & 7u; #ifdef @ENABLE_PATH_CLIPPING uint clipIDBits = (pathParams >> 4) & 0xffffu; - FLD(varyings, clipID) = - clipIDBits == 0u - ? .0 - : unpackHalf2x16((clipIDBits + MAX_DENORM_F16) * uniforms.pathIDGranularity).r; + v_clipID = clipIDBits == 0u + ? .0 + : unpackHalf2x16((clipIDBits + MAX_DENORM_F16) * uniforms.pathIDGranularity).r; // Negative clipID means to repalce the clip with this clipID. if (paintType == CLIP_REPLACE_PAINT_TYPE) - FLD(varyings, clipID) = -FLD(varyings, clipID); + v_clipID = -v_clipID; #endif #ifdef @ENABLE_ADVANCED_BLEND - FLD(varyings, blendMode) = float(pathParams & 0xfu); + v_blendMode = float(pathParams & 0xfu); #endif // Unpack the paint once we have a position. - uint4 paintData = TEXEL_FETCH(textures, @pathTexture, pathTexelCoord + int2(2, 0), 0); + uint4 paintData = TEXEL_FETCH(textures, @pathTexture, pathTexelCoord + int2(2, 0)); float4 paint; if (paintType != LINEAR_GRADIENT_PAINT_TYPE && paintType != RADIAL_GRADIENT_PAINT_TYPE) { @@ -334,7 +349,7 @@ paint.b = x0 * (1. / GRAD_TEXTURE_WIDTH) + (.5 / GRAD_TEXTURE_WIDTH); if (x1 > x0 + 1.) ++paint.b; // x1 must equal GRAD_TEXTURE_WIDTH - 1. Increment paint.b to convey this. - float2 localCoord = inverse(matrix) * (vertexPosition - translate); + float2 localCoord = MUL(inverse(mat), vertexPosition - translate); float3 gradCoeffs = uintBitsToFloat(paintData.yzw); if (paintType == LINEAR_GRADIENT_PAINT_TYPE) { @@ -351,40 +366,68 @@ paint.rg = (localCoord - gradCoeffs.xy) / gradCoeffs.z; } } - FLD(varyings, varying_paint) = paint; + v_paint = paint; - POSITION.xy = vertexPosition * float2(uniforms.renderTargetInverseViewportX, - -uniforms.renderTargetInverseViewportY) + - float2(-1, 1); - POSITION.zw = float2(0, 1); + _pos.xy = vertexPosition * float2(uniforms.renderTargetInverseViewportX, + -uniforms.renderTargetInverseViewportY) + + float2(-1, 1); + _pos.zw = float2(0, 1); if (shouldDiscardVertex) - POSITION = float4(uniforms.vertexDiscardValue); - EMIT_VERTEX(varyings); + { + _pos = float4(uniforms.vertexDiscardValue, + uniforms.vertexDiscardValue, + uniforms.vertexDiscardValue, + uniforms.vertexDiscardValue); + } + + VARYING_PACK(varyings, v_paint); +#ifdef @DRAW_INTERIOR_TRIANGLES + VARYING_PACK(varyings, v_windingWeight); +#else + VARYING_PACK(varyings, v_edgeDistance); +#endif + VARYING_PACK(varyings, v_pathID); +#ifdef @ENABLE_PATH_CLIPPING + VARYING_PACK(varyings, v_clipID); +#endif +#ifdef @ENABLE_ADVANCED_BLEND + VARYING_PACK(varyings, v_blendMode); +#endif + EMIT_VERTEX(varyings, _pos); } #endif #ifdef @FRAGMENT FRAG_TEXTURE_BLOCK_BEGIN(FragmentTextures) -TEXTURE_RGBA8(3) @gradTexture; +TEXTURE_RGBA8(3, @gradTexture); FRAG_TEXTURE_BLOCK_END +GRADIENT_SAMPLER_DECL(0, gradSampler) + PLS_BLOCK_BEGIN -PLS_DECL4F(0) framebuffer; -PLS_DECL2F(1) coverageCountBuffer; -PLS_DECL4F(2) originalDstColorBuffer; -PLS_DECL2F(3) clipBuffer; +PLS_DECL4F(0, framebuffer); +PLS_DECL2F(1, coverageCountBuffer); +PLS_DECL4F(2, originalDstColorBuffer); +PLS_DECL2F(3, clipBuffer); PLS_BLOCK_END -#ifdef METAL -PLS_MAIN(@drawFragmentMain, - Varyings varyings [[stage_in]], - FragmentTextures textures, - bool FRONT_FACING [[front_facing]]) -#else -PLS_MAIN() -#endif +PLS_MAIN(@drawFragmentMain, Varyings, varyings, FragmentTextures, textures, _pos, _clockwise) { - float4 paint = FLD(varyings, varying_paint); + VARYING_UNPACK(varyings, v_paint, float4); +#ifdef @DRAW_INTERIOR_TRIANGLES + VARYING_UNPACK(varyings, v_windingWeight, half); +#else + VARYING_UNPACK(varyings, v_edgeDistance, half2); +#endif + VARYING_UNPACK(varyings, v_pathID, half); +#ifdef @ENABLE_PATH_CLIPPING + VARYING_UNPACK(varyings, v_clipID, half); +#endif +#ifdef @ENABLE_ADVANCED_BLEND + VARYING_UNPACK(varyings, v_blendMode, half); +#endif + + float4 paint = v_paint; half4 color; if (paint.a >= .0) { @@ -403,7 +446,8 @@ ? ((GRAD_TEXTURE_WIDTH - .5) / GRAD_TEXTURE_WIDTH) // The ramp spans an entire row. : x0 + (1. / GRAD_TEXTURE_WIDTH); // The ramp spans 2 texels. float row = -paint.a; - color = TEXTURE_SAMPLE(textures, @gradTexture, float2(mix(x0, x1, t), row)); + float2 gradCoord = float2(mix(x0, x1, t), row); + color = make_half4(TEXTURE_SAMPLE(textures, @gradTexture, gradSampler, gradCoord)); } #ifndef @DRAW_INTERIOR_TRIANGLES @@ -416,7 +460,7 @@ half coverageCount = coverageData.g; half4 dstColor; - if (localPathID != FLD(varyings, pathID)) + if (localPathID != v_pathID) { // This is the first fragment from pathID to touch this pixel. coverageCount = .0; @@ -437,34 +481,34 @@ } #ifdef @DRAW_INTERIOR_TRIANGLES - coverageCount += FLD(varyings, windingWeight); + coverageCount += v_windingWeight; #else // TODO: We may need to just send actual flags instead of using sign(edgeDistance) to identify // strokes. Since edgeDistance is interpolated, it can sometimes cross signs. - half d = FLD(varyings, edgeDistance).x; + half d = v_edgeDistance.x; if (d < -1e-4 /*stroke with an intentionally negative edgeDistance*/) - coverageCount = min(max(d, FLD(varyings, edgeDistance).y), coverageCount); - else if (FRONT_FACING /*clockwise fill*/) + coverageCount = min(max(d, v_edgeDistance.y), coverageCount); + else if (_clockwise /*clockwise fill*/) coverageCount += d; else /*counterclockwise fill*/ coverageCount -= d; // Save the updated coverage. - PLS_STORE2F(coverageCountBuffer, FLD(varyings, pathID), coverageCount); + PLS_STORE2F(coverageCountBuffer, v_pathID, coverageCount); #endif // Convert coverageCount to coverage. (Which is min(-edgeDistance) right now for strokes.) half coverage = abs(coverageCount); #ifdef @ENABLE_EVEN_ODD - if (FLD(varyings, pathID) < .0 /*even-odd*/) + if (v_pathID < .0 /*even-odd*/) coverage = 1. - abs(fract(coverage * .5) * 2. + -1.); #endif coverage = min(coverage, make_half(1.)); // This also caps stroke coverage, which can be >1. #ifdef @ENABLE_PATH_CLIPPING - if (FLD(varyings, clipID) < .0 /*replace clip*/) + if (v_clipID < .0 /*replace clip*/) { - PLS_STORE2F(clipBuffer, -FLD(varyings, clipID), coverage); + PLS_STORE2F(clipBuffer, -v_clipID, coverage); PLS_PRESERVE_VALUE(framebuffer); } else @@ -472,10 +516,10 @@ { #ifdef @ENABLE_PATH_CLIPPING // Apply the clip. - if (FLD(varyings, clipID) > .0) + if (v_clipID > .0) { half2 clipData = PLS_LOAD2F(clipBuffer); - coverage = FLD(varyings, clipID) == clipData.r ? min(coverage, clipData.g) : .0; + coverage = v_clipID == clipData.r ? min(coverage, clipData.g) : .0; } #endif PLS_PRESERVE_VALUE(clipBuffer); @@ -483,7 +527,7 @@ // Blend with the framebuffer color. color.a *= coverage; #ifdef @ENABLE_ADVANCED_BLEND - if (FLD(varyings, blendMode) != .0 /*srcOver*/) + if (v_blendMode != .0 /*srcOver*/) { #ifdef @ENABLE_HSL_BLEND_MODES color = advanced_hsl_blend( @@ -492,7 +536,7 @@ #endif color, unmultiply(dstColor), - make_ushort(FLD(varyings, blendMode))); + make_ushort(v_blendMode)); } else #endif
diff --git a/renderer/shaders/glsl.glsl b/renderer/shaders/glsl.glsl index 5b6d377..c1254a0 100644 --- a/renderer/shaders/glsl.glsl +++ b/renderer/shaders/glsl.glsl
@@ -5,6 +5,8 @@ // This header provides GLSL-specific #defines and declarations that enable our shaders to be // compiled on MSL and GLSL both. +#define GLSL + #define float2 vec2 #define float3 vec3 #define packed_float3 vec3 @@ -46,15 +48,9 @@ #define make_ushort4 uvec4 #define float2x2 mat2 -#define float4x2 mat4x2 -#define make_float2x2 mat2 -#define make_float4x2 mat4x2 #define make_half3x4 mat3x4 #define INLINE -#define FRONT_FACING gl_FrontFacing -#define POSITION gl_Position -#define VERTEX_ID gl_VertexID #if defined(GL_ANGLE_base_vertex_base_instance_shader_builtin) #extension GL_ANGLE_base_vertex_base_instance_shader_builtin : require @@ -70,23 +66,24 @@ layout(std140) uniform N \ { // clang-format barrier... Otherwise it tries to merge this #define into the above macro... -#define UNIFORM_BLOCK_END(N) \ +#define UNIFORM_BLOCK_END(NAME) \ } \ - N; + NAME; -#define ATTR_BLOCK_BEGIN(N) -#define ATTR(L) layout(location = L) in +#define ATTR_BLOCK_BEGIN(NAME) +#define ATTR(IDX, TYPE, NAME) layout(location = IDX) in TYPE NAME #define ATTR_BLOCK_END #define ATTR_LOAD(A, B, C, D) +#define ATTR_UNPACK(ID, attrs, NAME, TYPE) -#define VARYING_BLOCK_BEGIN(N) #ifdef @VERTEX -#define VARYING out +#define VARYING(TYPE, NAME) out TYPE NAME #else -#define VARYING in +#define VARYING(TYPE, NAME) in TYPE NAME #endif #define FLAT flat -#define VARYING_BLOCK_END +#define VARYING_BLOCK_BEGIN(NAME) +#define VARYING_BLOCK_END(_pos) #if defined(GL_NV_shader_noperspective_interpolation) #extension GL_NV_shader_noperspective_interpolation : require @@ -96,21 +93,22 @@ #endif #ifdef @VERTEX -#define VERTEX_TEXTURE_BLOCK_BEGIN(N) +#define VERTEX_TEXTURE_BLOCK_BEGIN(NAME) #define VERTEX_TEXTURE_BLOCK_END #endif #ifdef @FRAGMENT -#define FRAG_TEXTURE_BLOCK_BEGIN(N) +#define FRAG_TEXTURE_BLOCK_BEGIN(NAME) #define FRAG_TEXTURE_BLOCK_END #endif -#define TEXTURE_RGBA32UI(B) uniform highp usampler2D -#define TEXTURE_RGBA32F(B) uniform highp sampler2D -#define TEXTURE_RGBA8(B) uniform mediump sampler2D +#define TEXTURE_RGBA32UI(IDX, NAME) uniform highp usampler2D NAME +#define TEXTURE_RGBA32F(IDX, NAME) uniform highp sampler2D NAME +#define TEXTURE_RGBA8(IDX, NAME) uniform mediump sampler2D NAME -#define TEXEL_FETCH(T, N, C, L) texelFetch(N, C, L) -#define TEXTURE_SAMPLE(T, N, C) texture(N, C) +#define TEXEL_FETCH(TEXTURE_BLOCK, NAME, COORD) texelFetch(NAME, COORD, 0) +#define TEXTURE_SAMPLE(TEXTURE_BLOCK, NAME, SAMPLER_NAME, COORD) texture(NAME, COORD) +#define GRADIENT_SAMPLER_DECL(IDX, NAME) // Define macros for implementing pixel local storage based on available extensions. #ifdef @PLS_IMPL_WEBGL @@ -118,8 +116,8 @@ #extension GL_ANGLE_shader_pixel_local_storage : require #define PLS_BLOCK_BEGIN -#define PLS_DECL4F(B) layout(binding = B, rgba8) uniform lowp pixelLocalANGLE -#define PLS_DECL2F(B) layout(binding = B, r32ui) uniform highp upixelLocalANGLE +#define PLS_DECL4F(IDX, NAME) layout(binding = IDX, rgba8) uniform lowp pixelLocalANGLE NAME +#define PLS_DECL2F(IDX, NAME) layout(binding = IDX, r32ui) uniform highp upixelLocalANGLE NAME #define PLS_BLOCK_END #define PLS_LOAD4F(P) pixelLocalLoadANGLE(P) @@ -145,8 +143,8 @@ #define PLS_BLOCK_BEGIN \ __pixel_localEXT PLS \ { -#define PLS_DECL4F(B) layout(rgba8) lowp vec4 -#define PLS_DECL2F(B) layout(rg16f) mediump vec2 +#define PLS_DECL4F(IDX, NAME) layout(rgba8) lowp vec4 NAME +#define PLS_DECL2F(IDX, NAME) layout(rg16f) mediump vec2 NAME #define PLS_BLOCK_END \ } \ ; @@ -168,8 +166,8 @@ #extension GL_EXT_shader_framebuffer_fetch : require #define PLS_BLOCK_BEGIN -#define PLS_DECL4F(B) layout(location = B) inout lowp vec4 -#define PLS_DECL2F(B) layout(location = B) inout highp uvec4 +#define PLS_DECL4F(IDX, NAME) layout(location = IDX) inout lowp vec4 NAME +#define PLS_DECL2F(IDX, NAME) layout(location = IDX) inout highp uvec4 NAME #define PLS_BLOCK_END #define PLS_LOAD4F(P) P @@ -199,8 +197,8 @@ #endif #define PLS_BLOCK_BEGIN -#define PLS_DECL4F(B) layout(binding = B, rgba8) uniform lowp coherent image2D -#define PLS_DECL2F(B) layout(binding = B, r32ui) uniform highp coherent uimage2D +#define PLS_DECL4F(IDX, NAME) layout(binding = IDX, rgba8) uniform lowp coherent image2D NAME +#define PLS_DECL2F(IDX, NAME) layout(binding = IDX, r32ui) uniform highp coherent uimage2D NAME #define PLS_BLOCK_END #define PLS_LOAD4F(P) imageLoad(P, plsCoord) @@ -212,29 +210,66 @@ #endif -#define VERTEX_MAIN void main -#define EMIT_VERTEX(v) -#define EMIT_OFFSCREEN_VERTEX(v) - -#define FRAG_DATA_TYPE(T) out T _fd; -#define FRAG_DATA_MAIN void main -#define EMIT_FRAG_DATA(f) _fd = f - -#ifdef @PLS_IMPL_RW_TEXTURE -#define PLS_MAIN() \ +#ifdef @ENABLE_BASE_INSTANCE_POLYFILL +#define BASE_INSTANCE_POLYFILL_DECL(NAME) uniform int NAME +// The Qualcomm compiler doesn't like how clang-format handles these lines. +// clang-format off +#define VERTEX_MAIN(NAME, Uniforms, uniforms, Attrs, attrs, Varyings, varyings, VertexTextures, textures, _vertexID, _instanceID, _pos) \ void main() \ { \ - highp ivec2 plsCoord = ivec2(floor(gl_FragCoord.xy)); -#define EMIT_PLS } + int _vertexID = gl_VertexID; \ + int _instanceID = gl_InstanceID; \ + vec4 _pos; +// clang-format on #else -#define PLS_MAIN void main -#define EMIT_PLS +#ifdef GL_ANGLE_base_vertex_base_instance_shader_builtin +#extension GL_ANGLE_base_vertex_base_instance_shader_builtin : require +#endif +// The Qualcomm compiler doesn't like how clang-format handles these lines. +// clang-format off +#define VERTEX_MAIN(NAME, Uniforms, uniforms, Attrs, attrs, Varyings, varyings, VertexTextures, textures, _vertexID, _instanceID, _pos) \ + void main() \ + { \ + int _vertexID = gl_VertexID; \ + int _instanceID = gl_BaseInstance + gl_InstanceID; \ + vec4 _pos; +// clang-format on #endif -// "FLD" for "field". -// In Metal, inputs and outputs are accessed from structs. -// In GLSL 300 es, they have to be global. -#define FLD(BLOCK, MEMBER) MEMBER +#define VARYING_INIT(varyings, NAME, TYPE) +#define VARYING_PACK(varyings, NAME) +#define VARYING_UNPACK(varyings, NAME, TYPE) + +#define EMIT_VERTEX(varyings, _pos) \ + } \ + gl_Position = _pos; + +#define EMIT_OFFSCREEN_VERTEX(varyings, _pos) \ + } \ + gl_Position = _pos; + +#define FRAG_DATA_MAIN(DATA_TYPE, NAME, Varyings, varyings) \ + out DATA_TYPE _fd; \ + void main() + +#define EMIT_FRAG_DATA(VALUE) _fd = VALUE + +#ifdef @PLS_IMPL_RW_TEXTURE +#define PLS_MAIN(NAME, Varyings, varyings, FragmentTextures, textures, _pos, _clockwise) \ + void main() \ + { \ + bool _clockwise = gl_FrontFacing; \ + highp ivec2 plsCoord = ivec2(floor(gl_FragCoord.xy)); +#else +#define PLS_MAIN(NAME, Varyings, varyings, FragmentTextures, textures, _pos, _clockwise) \ + void main() \ + { \ + bool _clockwise = gl_FrontFacing; +#endif + +#define EMIT_PLS } + +#define MUL(A, B) ((A) * (B)) precision highp float; precision highp int;
diff --git a/renderer/shaders/hlsl.glsl b/renderer/shaders/hlsl.glsl new file mode 100644 index 0000000..99003c9 --- /dev/null +++ b/renderer/shaders/hlsl.glsl
@@ -0,0 +1,237 @@ +/* + * Copyright 2023 Rive + */ + +// This header provides GLSL-specific #defines and declarations that enable our shaders to be +// compiled on MSL and GLSL both. + +// HLSL warns that it will unroll the loops through r,g,b values in advanced_blend.glsl, but +// unrolling these loops is exactly what we want. +#pragma warning(disable : 3550) + +// #define native hlsl types if their names are being rewritten. +#define _ARE_TOKEN_NAMES_PRESERVED +#ifndef $_ARE_TOKEN_NAMES_PRESERVED +#define half half +#define half2 half2 +#define half3 half3 +#define half4 half4 +#define short short +#define short2 short2 +#define short3 short3 +#define short4 short4 +#define ushort ushort +#define ushort2 ushort2 +#define ushort3 ushort3 +#define ushort4 ushort4 +#define float2 float2 +#define float3 float3 +#define float4 float4 +#define bool2 bool2 +#define bool3 bool3 +#define bool4 bool4 +#define uint2 uint2 +#define uint3 uint3 +#define uint4 uint4 +#define int2 int2 +#define int3 int3 +#define int4 int4 +#define float4x2 float4x2 +#define ushort ushort +#define float2x2 float2x2 +#define half3x4 half3x4 +#endif + +typedef float3 packed_float3; +#define make_half float +#define make_half2 float2 +#define make_half3 float3 +#define make_half4 float4 + +typedef min16int short; +#define make_short min16int +#define make_short2 min16int2 +#define make_short3 min16int3 +#define make_short4 min16int4 + +typedef min16uint ushort; +#define make_ushort min16uint +#define make_ushort2 min16uint2 +#define make_ushort3 min16uint3 +#define make_ushort4 min16uint4 + +#define make_half3x4 float3x4 + +#define ATTR_BLOCK_BEGIN(NAME) \ + struct NAME \ + { +#define ATTR(IDX, TYPE, NAME) TYPE NAME : NAME +#define ATTR_BLOCK_END \ + } \ + ; +#define ATTR_LOAD(T, A, N, I) +#define ATTR_UNPACK(ID, attrs, NAME, TYPE) TYPE NAME = attrs.NAME + +#define UNIFORM_BLOCK_BEGIN(NAME) \ + cbuffer NAME : register(b0) \ + { \ + struct \ + { + +#define UNIFORM_BLOCK_END(NAME) \ + } \ + NAME; \ + } + +#define VARYING_BLOCK_BEGIN(NAME) \ + struct NAME \ + { + +#define NO_PERSPECTIVE noperspective +#define @OPTIONALLY_FLAT nointerpolation +#define FLAT nointerpolation +#define VARYING(TYPE, NAME) TYPE NAME : NAME + +#define VARYING_BLOCK_END(_pos) \ + float4 _pos : SV_Position; \ + } \ + ; + +#define VARYING_INIT(varyings, NAME, TYPE) TYPE NAME +#define VARYING_PACK(varyings, NAME) varyings.NAME = NAME +#define VARYING_UNPACK(varyings, NAME, TYPE) TYPE NAME = varyings.NAME + +#ifdef @VERTEX +#define VERTEX_TEXTURE_BLOCK_BEGIN(NAME) +#define VERTEX_TEXTURE_BLOCK_END +#endif + +#ifdef @FRAGMENT +#define FRAG_TEXTURE_BLOCK_BEGIN(NAME) +#define FRAG_TEXTURE_BLOCK_END +#endif + +#define TEXTURE_RGBA32UI(IDX, NAME) uniform Texture2D<uint4> NAME : register(t##IDX) +#define TEXTURE_RGBA32F(IDX, NAME) uniform Texture2D<float4> NAME : register(t##IDX) +#define TEXTURE_RGBA8(IDX, NAME) uniform Texture2D<unorm float4> NAME : register(t##IDX) + +#define TEXEL_FETCH(TEXTURE_BLOCK, NAME, COORD) NAME[COORD] + +#define GRADIENT_SAMPLER_DECL(IDX, NAME) SamplerState NAME : register(s##IDX); + +#define TEXTURE_SAMPLE(TEXTURE_BLOCK, NAME, SAMPLER_NAME, COORD) NAME.Sample(SAMPLER_NAME, COORD) + +#define PLS_INTERLOCK_BEGIN +#define PLS_INTERLOCK_END + +#ifdef @DRAW_INTERIOR_TRIANGLES +// The interior triangles don't overlap, so therefore don't need rasterizer ordering. +#define $PLS_TEX2D RWTexture2D +#else +#define $PLS_TEX2D RasterizerOrderedTexture2D +#endif + +#define PLS_BLOCK_BEGIN +#define PLS_DECL4F(IDX, NAME) uniform PLS_TEX2D<unorm float4> NAME : register(u##IDX) +#define PLS_DECL2F(IDX, NAME) uniform PLS_TEX2D<uint> NAME : register(u##IDX) +#define PLS_BLOCK_END + +#define PLS_LOAD4F(P) P[_plsCoord] +#define PLS_LOAD2F(P) unpackHalf2x16(P[_plsCoord]) +#define PLS_STORE4F(P, V) P[_plsCoord] = V +#define PLS_STORE2F(P, X, Y) P[_plsCoord] = packHalf2x16(float2(X, Y)) + +#define PLS_PRESERVE_VALUE(P) + +#ifdef @ENABLE_BASE_INSTANCE_POLYFILL +#define BASE_INSTANCE_POLYFILL_DECL(NAME) \ + cbuffer NAME##_cbuff : register(b1) \ + { \ + uint NAME; \ + uint NAME##_pad0; \ + uint NAME##_pad1; \ + uint NAME##_pad2; \ + } + +#endif + +#define VERTEX_MAIN(NAME, \ + Uniforms, \ + uniforms, \ + Attrs, \ + attrs, \ + Varyings, \ + varyings, \ + VertexTextures, \ + textures, \ + _vertexID, \ + _instanceID, \ + _pos) \ + Varyings NAME(Attrs attrs, uint _vertexID : SV_VertexID, uint _instanceID : SV_InstanceID) \ + { \ + Varyings varyings; \ + float4 _pos; + +#define EMIT_VERTEX(varyings, _pos) \ + } \ + varyings._pos = _pos; \ + return varyings; + +#define EMIT_OFFSCREEN_VERTEX(varyings, _pos) \ + varyings._pos.xzw = _pos.xzw; \ + varyings._pos.y = -_pos.y; \ + return varyings; \ + } + +#define FRAG_DATA_MAIN(DATA_TYPE, NAME, Varyings, varyings) \ + DATA_TYPE NAME(Varyings varyings) : SV_Target \ + { + +#define EMIT_FRAG_DATA(VALUE) \ + return VALUE; \ + } + +#define PLS_MAIN(NAME, Varyings, varyings, FragmentTextures, textures, _pos, _clockwise) \ + [earlydepthstencil] void NAME(Varyings varyings, bool _clockwise : SV_IsFrontFace) { \ + int2 _plsCoord = int2(floor(varyings._pos.xy)); + +#define EMIT_PLS } + +#define INLINE inline + +#define uintBitsToFloat asfloat +#define intBitsToFloat asfloat +#define floatBitsToInt asint +#define floatBitsToUint asuint +#define fract frac +#define mix lerp +#define inversesqrt rsqrt +#define notEqual(A, B) ((A) != (B)) +#define lessThanEqual(A, B) ((A) <= (B)) +#define greaterThanEqual(A, B) ((A) >= (B)) + +// HLSL matrices are stored in row-major order, and therefore transposed from their counterparts +// in GLSL and Metal. We can work around this entirely by reversing the arguments to mul(). +#define MUL(A, B) mul(B, A) + +inline half2 unpackHalf2x16(uint u) +{ + uint y = (u >> 16); + uint x = u & 0xffffu; + return make_half2(f16tof32(x), f16tof32(y)); +} + +inline uint packHalf2x16(float2 v) +{ + uint x = f32tof16(v.x); + uint y = f32tof16(v.y); + return (y << 16) | x; +} + +inline float atan(float y, float x) { return atan2(y, x); } + +inline float2x2 inverse(float2x2 m) +{ + float2x2 adjoint = float2x2(m[1][1], -m[0][1], -m[1][0], m[0][0]); + return adjoint * (1. / determinant(m)); +}
diff --git a/renderer/shaders/metal.glsl b/renderer/shaders/metal.glsl index 1cd6abb..8a147ea 100644 --- a/renderer/shaders/metal.glsl +++ b/renderer/shaders/metal.glsl
@@ -7,7 +7,9 @@ #define METAL -// Native metal types need a #define. They get rewritten because they are not GLSL keywords. +// #define native metal types if their names are being rewritten. +#define _ARE_TOKEN_NAMES_PRESERVED +#ifndef $_ARE_TOKEN_NAMES_PRESERVED #define half half #define half2 half2 #define half3 half3 @@ -37,6 +39,7 @@ #define ushort ushort #define float2x2 float2x2 #define half3x4 half3x4 +#endif #define make_half4 half4 #define make_half3 half3 @@ -50,6 +53,7 @@ #define notEqual(A, B) ((A) != (B)) #define lessThanEqual(A, B) ((A) <= (B)) #define greaterThanEqual(A, B) ((A) >= (B)) +#define MUL(A, B) ((A) * (B)) #define atan atan2 #define inversesqrt rsqrt @@ -63,27 +67,31 @@ #define ATTR_BLOCK_BEGIN(N) \ struct N \ { -#define ATTR(LOCATION) +#define ATTR(IDX, TYPE, NAME) TYPE NAME #define ATTR_BLOCK_END \ } \ ; -#define ATTR_LOAD(T, A, N, I) T N = A[I].N; +#define ATTR_UNPACK(ID, attrs, NAME, TYPE) TYPE NAME = attrs[ID].NAME #define VARYING_BLOCK_BEGIN(N) \ struct N \ { -#define VARYING +#define VARYING(TYPE, NAME) TYPE NAME #define FLAT [[flat]] #define NO_PERSPECTIVE [[center_no_perspective]] // No-persective interpolation appears to break the guarantee that a varying == "x" when all // barycentric values also == "x". Using default (perspective-correct) interpolation is also faster // than flat on M1. #define @OPTIONALLY_FLAT -#define VARYING_BLOCK_END \ +#define VARYING_BLOCK_END(_pos) \ float4 _pos [[position]]; \ } \ ; +#define VARYING_INIT(varyings, NAME, TYPE) thread TYPE& NAME = varyings.NAME +#define VARYING_PACK(varyings, NAME) +#define VARYING_UNPACK(varyings, NAME, TYPE) TYPE NAME = varyings.NAME + #define VERTEX_TEXTURE_BLOCK_BEGIN(N) \ struct N \ { @@ -98,18 +106,20 @@ } \ ; -#define TEXTURE_RGBA32UI(B) [[texture(B)]] texture2d<uint> -#define TEXTURE_RGBA32F(B) [[texture(B)]] texture2d<float> -#define TEXTURE_RGBA8(B) [[texture(B)]] texture2d<half> +#define TEXTURE_RGBA32UI(IDX, NAME) [[texture(IDX)]] texture2d<uint> NAME +#define TEXTURE_RGBA32F(IDX, NAME) [[texture(IDX)]] texture2d<float> NAME +#define TEXTURE_RGBA8(IDX, NAME) [[texture(IDX)]] texture2d<half> NAME -#define TEXEL_FETCH(T, N, C, L) T.N.read(uint2(C)) -#define TEXTURE_SAMPLE(T, N, C) T.N.sample(sampler(mag_filter::linear, min_filter::linear), C) +#define TEXEL_FETCH(TEXTURE_BLOCK, NAME, COORD) TEXTURE_BLOCK.NAME.read(uint2(COORD)) +#define GRADIENT_SAMPLER_DECL(IDX, NAME) +#define TEXTURE_SAMPLE(TEXTURE_BLOCK, NAME, SAMPLER_NAME, COORD) \ + TEXTURE_BLOCK.NAME.sample(sampler(mag_filter::linear, min_filter::linear), COORD) #define PLS_BLOCK_BEGIN \ struct PLS \ { -#define PLS_DECL4F(B) [[color(B)]] half4 -#define PLS_DECL2F(B) [[color(B)]] half2 +#define PLS_DECL4F(IDX, NAME) [[color(IDX)]] half4 NAME +#define PLS_DECL2F(IDX, NAME) [[color(IDX)]] half2 NAME #define PLS_BLOCK_END \ } \ ; @@ -122,47 +132,58 @@ #define PLS_INTERLOCK_BEGIN #define PLS_INTERLOCK_END -// A hack since we use POSITION inside the following #defines... -#define POSITION POSITION - -#define VERTEX_MAIN(F, V, v, ...) \ - __attribute__((visibility("default"))) V vertex F(__VA_ARGS__) \ +#define VERTEX_MAIN(NAME, \ + Uniforms, \ + uniforms, \ + Attrs, \ + attrs, \ + Varyings, \ + varyings, \ + VertexTextures, \ + textures, \ + _vertexID, \ + _instanceID, \ + _pos) \ + __attribute__((visibility("default"))) Varyings vertex NAME( \ + uint _vertexID [[vertex_id]], \ + uint _instanceID [[instance_id]], \ + constant Uniforms& uniforms [[buffer(0)]], \ + constant Attrs* attrs [[buffer(1)]], \ + VertexTextures textures) \ { \ - V v; \ - float4 POSITION; + Varyings varyings; \ + float4 _pos; -#define EMIT_VERTEX(v) \ - v._pos = POSITION; \ - return v; \ +#define EMIT_VERTEX(varyings, _pos) \ + } \ + varyings._pos = _pos; \ + return varyings; + +#define EMIT_OFFSCREEN_VERTEX(varyings, _pos) \ + varyings._pos.xzw = _pos.xzw; \ + varyings._pos.y = -_pos.y; \ + return varyings; \ } -#define EMIT_OFFSCREEN_VERTEX(v) \ - v._pos = POSITION; \ - v._pos.y = -v._pos.y; \ - return v; \ - } - -#define FRAG_DATA_TYPE(T) T -#define FRAG_DATA_MAIN(F, ...) \ - __attribute__((visibility("default"))) fragment F(__VA_ARGS__) \ +#define FRAG_DATA_MAIN(DATA_TYPE, NAME, Varyings, varyings) \ + DATA_TYPE __attribute__((visibility("default"))) fragment NAME(Varyings varyings [[stage_in]]) \ { -#define EMIT_FRAG_DATA(f) \ - return f; \ + +#define EMIT_FRAG_DATA(VALUE) \ + return VALUE; \ } -#define PLS_MAIN(F, ...) \ - __attribute__((visibility("default"))) PLS fragment F(PLS _inpls, __VA_ARGS__) \ +#define PLS_MAIN(NAME, Varyings, varyings, FragmentTextures, textures, _pos, _clockwise) \ + __attribute__((visibility("default"))) PLS fragment NAME(PLS _inpls, \ + Varyings varyings [[stage_in]], \ + bool _clockwise [[front_facing]], \ + FragmentTextures textures) \ { \ PLS _pls; #define EMIT_PLS \ - return _pls; \ - } - -// "FLD" for "field". -// In Metal, inputs and outputs are accessed from structs. -// In GLSL 300 es, they have to be global. -#define FLD(BLOCK, MEMBER) BLOCK.MEMBER + } \ + return _pls; using namespace metal; @@ -196,9 +217,6 @@ return m_ * (1 / det); } -inline float2x2 make_float2x2(float4 f) { return float2x2(f.xy, f.zw); } -inline float4x2 make_float4x2(float4 a, float4 b) { return float4x2(a.xy, a.zw, b.xy, b.zw); } - inline half3 mix(half3 a, half3 b, bool3 c) { half3 result;
diff --git a/renderer/shaders/minify.py b/renderer/shaders/minify.py index df0a039..c427ec4 100644 --- a/renderer/shaders/minify.py +++ b/renderer/shaders/minify.py
@@ -18,6 +18,8 @@ - No new name includes the '_' character, so internal code can use '_' without fear of renaming collisions. - GLSL keywords and builtins are not renamed. + - Tokens beginning with '@' have their new name exported to a header file. + - Tokens beginning with '$' are not renamed, with the exception of removing the leading '$'. "file.glsl" gets exported to: * "outdir/file.exports.h", with #defines for the rewritten names of each identifier that began @@ -65,7 +67,7 @@ # lexing functions used by PLY. def t_DEFINE(tok): - r"\#[ \t]*define[ \t]+(?P<id>\@?[A-Za-z_][A-Za-z0-9_]*)(?P<val>(((\\\n|.)(?!\/[\/\*]))*))" + r"\#[ \t]*define[ \t]+(?P<id>[\@\$]?[A-Za-z_][A-Za-z0-9_]*)(?P<val>(((\\\n|.)(?!\/[\/\*]))*))" tok.define_id = re.match(t_DEFINE.__doc__, tok.value)['id'] tok.define_val = re.match(t_DEFINE.__doc__, tok.value)['val'] defines.add(tok.define_id) @@ -74,7 +76,7 @@ return tok def t_IFDEF(tok): - r"(?P<tag>\#[ \t]*ifn?def)[ \t]+(?P<id>\@?[A-Za-z_][A-Za-z0-9_]*)" + r"(?P<tag>\#[ \t]*ifn?def)[ \t]+(?P<id>[\@\$]?[A-Za-z_][A-Za-z0-9_]*)" tok.ifdef_tag = re.match(t_IFDEF.__doc__, tok.value)['tag'] tok.ifdef_id = re.match(t_IFDEF.__doc__, tok.value)['id'] parse_id(tok.ifdef_id, tok.lexer.exports) @@ -122,7 +124,7 @@ return tok def t_ID(tok): - r"\@?[A-Za-z_][A-Za-z0-9_]*" + r"[\@\$]?[A-Za-z_][A-Za-z0-9_]*" parse_id(tok.value, tok.lexer.exports) return tok @@ -274,6 +276,10 @@ # keywords borrowed from metal, used by metal.glsl "using", "namespace", "metal", "inline", "template", "vec", "as_type", "constant", + + # keywords borrowed from hlsl, used by hlsl.glsl + "f16tof32", "f32tof16", "atan2", "asfloat", "asint", "asuint", "frac", "lerp", "rsqrt", + "typedef", "min16int", "min16uint" } # rgba and stpq get rewritten to xyzw, so we only need to check xyzw here. This way we can keep @@ -284,14 +290,16 @@ def is_reserved_keyword(name): return name in glsl_reserved\ or xyzw_pattern.match(name)\ + or name.startswith("$")\ or name.startswith("gl_")\ or name.startswith("__pixel_local")\ or name.endswith("ANGLE") -# A leading '@' indicates identifier names that should be exported. This method removes it, if it -# exists. -def remove_leading_at(name): - return name[1:] if name[0] == '@' else name +# A leading '@' indicates identifier names that should be exported. +# A leading '$' indicates identifier names that should not be renamed. +# This method removes both, if they exist. +def remove_leading_annotation(name): + return name[1:] if name[0] == '@' or name[0] == '$' else name # generates new identifier names to rewrite our variables. # exclude '_' from our new names. Internal variables can use '_' to avoid naming collisions. @@ -313,7 +321,7 @@ new_names = {} def generate_new_names(): for name,count in sorted(all_id_counts.items(), key=lambda x:x[1], reverse=True): - new_name = (remove_leading_at(name) + new_name = (remove_leading_annotation(name) if args.human_readable or is_reserved_keyword(name) else generate_new_name()) new_names[name] = new_name
diff --git a/renderer/shaders/tessellate.glsl b/renderer/shaders/tessellate.glsl index 04c3aef..4279f53 100644 --- a/renderer/shaders/tessellate.glsl +++ b/renderer/shaders/tessellate.glsl
@@ -13,35 +13,35 @@ #ifdef @VERTEX ATTR_BLOCK_BEGIN(Attrs) -ATTR(0) float4 attr_p0p1; -ATTR(1) float4 attr_p2p3; -ATTR(2) float4 attr_joinTangent; -ATTR(3) uint4 attr_args; // [x0, x1, y, polarSegmentCount, contourIDWithFlags] +ATTR(0, float4, @a_p0p1_); +ATTR(1, float4, @a_p2p3_); +ATTR(2, float4, @a_joinTangent); +ATTR(3, uint4, @a_args); // [x0, x1, y, polarSegmentCount, contourIDWithFlags] ATTR_BLOCK_END #endif VARYING_BLOCK_BEGIN(Varyings) -NO_PERSPECTIVE VARYING float4 p0p1; -NO_PERSPECTIVE VARYING float4 p2p3; -NO_PERSPECTIVE VARYING float4 args; // [vertexIdx, totalVertexCount, joinSegmentCount, - // parametricSegmentCount, radsPerPolarSegment] -NO_PERSPECTIVE VARYING float3 joinArgs; // [joinTangent, radsPerJoinSegment] -FLAT VARYING uint contourIDWithFlags; -VARYING_BLOCK_END +NO_PERSPECTIVE VARYING(float4, v_p0p1); +NO_PERSPECTIVE VARYING(float4, v_p2p3); +NO_PERSPECTIVE VARYING(float4, v_args); // [vertexIdx, totalVertexCount, joinSegmentCount, + // parametricSegmentCount, radsPerPolarSegment] +NO_PERSPECTIVE VARYING(float3, v_joinArgs); // [joinTangent, radsPerJoinSegment] +FLAT VARYING(uint, v_contourIDWithFlags); +VARYING_BLOCK_END(_pos) // Tangent of the curve at T=0 and T=1. -float2x2 find_tangents(float4x2 p) +INLINE float2x2 find_tangents(float2 p0, float2 p1, float2 p2, float2 p3) { float2x2 t; - t[0] = (any(notEqual(p[0], p[1])) ? p[1] : any(notEqual(p[1], p[2])) ? p[2] : p[3]) - p[0]; - t[1] = p[3] - (any(notEqual(p[3], p[2])) ? p[2] : any(notEqual(p[2], p[1])) ? p[1] : p[0]); + t[0] = (any(notEqual(p0, p1)) ? p1 : any(notEqual(p1, p2)) ? p2 : p3) - p0; + t[1] = p3 - (any(notEqual(p3, p2)) ? p2 : any(notEqual(p2, p1)) ? p1 : p0); return t; } #ifdef @VERTEX VERTEX_TEXTURE_BLOCK_BEGIN(VertexTextures) -TEXTURE_RGBA32UI(1) @pathTexture; -TEXTURE_RGBA32UI(2) @contourTexture; +TEXTURE_RGBA32UI(1, @pathTexture); +TEXTURE_RGBA32UI(2, @contourTexture); VERTEX_TEXTURE_BLOCK_END float cosine_between_vectors(float2 a, float2 b) @@ -52,46 +52,56 @@ return (ab_pow2 == .0) ? 1. : clamp(ab_cosTheta * inversesqrt(ab_pow2), -1., 1.); } -VERTEX_MAIN( -#ifdef METAL - @tessellateVertexMain, - Varyings, - varyings, - uint VERTEX_ID [[vertex_id]], - uint INSTANCE_ID [[instance_id]], - constant @Uniforms& uniforms [[buffer(0)]], - constant Attrs* attrs [[buffer(1)]], - VertexTextures textures -#endif -) +VERTEX_MAIN(@tessellateVertexMain, + @Uniforms, + uniforms, + Attrs, + attrs, + Varyings, + varyings, + VertexTextures, + textures, + _vertexID, + _instanceID, + _pos) { - ATTR_LOAD(float4, attrs, attr_p0p1, INSTANCE_ID); - ATTR_LOAD(float4, attrs, attr_p2p3, INSTANCE_ID); - ATTR_LOAD(uint4, attrs, attr_args, INSTANCE_ID); - ATTR_LOAD(float4, attrs, attr_joinTangent, INSTANCE_ID); + ATTR_UNPACK(_instanceID, attrs, @a_p0p1_, float4); + ATTR_UNPACK(_instanceID, attrs, @a_p2p3_, float4); + ATTR_UNPACK(_instanceID, attrs, @a_joinTangent, float4); + ATTR_UNPACK(_instanceID, attrs, @a_args, uint4); - float4x2 p = float4x2(attr_p0p1.xy, attr_p0p1.zw, attr_p2p3.xy, attr_p2p3.zw); - float x0 = float(int(attr_args.x << 16) >> 16); - float x1 = float(int(attr_args.x) >> 16); - float y = float(attr_args.y); - float2 coord = float2((VERTEX_ID & 1) == 0 ? x0 : x1, (VERTEX_ID & 2) == 0 ? y : y + 1.); + VARYING_INIT(varyings, v_p0p1, float4); + VARYING_INIT(varyings, v_p2p3, float4); + VARYING_INIT(varyings, v_args, float4); + VARYING_INIT(varyings, v_joinArgs, float3); + VARYING_INIT(varyings, v_contourIDWithFlags, uint); - uint parametricSegmentCount = attr_args.z & 0x3ffu; - uint polarSegmentCount = (attr_args.z >> 10) & 0x3ffu; - uint joinSegmentCount = attr_args.z >> 20; - uint outContourIDWithFlags = attr_args.w; - if ((outContourIDWithFlags & CULL_EXCESS_TESSELLATION_SEGMENTS_FLAG) != 0u) + float2 p0 = @a_p0p1_.xy; + float2 p1 = @a_p0p1_.zw; + float2 p2 = @a_p2p3_.xy; + float2 p3 = @a_p2p3_.zw; + float x0 = float(int(@a_args.x << 16) >> 16); + float x1 = float(int(@a_args.x) >> 16); + float y = float(@a_args.y); + float2 coord = float2((_vertexID & 1) == 0 ? x0 : x1, (_vertexID & 2) == 0 ? y : y + 1.); + + uint parametricSegmentCount = @a_args.z & 0x3ffu; + uint polarSegmentCount = (@a_args.z >> 10) & 0x3ffu; + uint joinSegmentCount = @a_args.z >> 20; + uint contourIDWithFlags = @a_args.w; + if ((contourIDWithFlags & CULL_EXCESS_TESSELLATION_SEGMENTS_FLAG) != 0u) { // This span may have more tessellation vertices allocated to it than necessary (e.g., // outerCurve patches all have a fixed patch size, regardless of how many segments the curve // actually needs). Re-run Wang's formula to figure out how many segments we actually need, // and make any excess segments degenerate by co-locating their vertices at T=0. uint pathIDBits = - TEXEL_FETCH(textures, @contourTexture, contour_texel_coord(outContourIDWithFlags), 0).z; - float2x2 matrix = make_float2x2( - uintBitsToFloat(TEXEL_FETCH(textures, @pathTexture, path_texel_coord(pathIDBits), 0))); - float2 d0 = matrix * (-2. * p[1] + p[2] + p[0]); - float2 d1 = matrix * (-2. * p[2] + p[3] + p[1]); + TEXEL_FETCH(textures, @contourTexture, contour_texel_coord(contourIDWithFlags)).z; + float2x2 mat = make_float2x2( + uintBitsToFloat(TEXEL_FETCH(textures, @pathTexture, path_texel_coord(pathIDBits)))); + float2 d0 = MUL(mat, -2. * p1 + p2 + p0); + + float2 d1 = MUL(mat, -2. * p2 + p3 + p1); float m = max(dot(d0, d0), dot(d1, d1)); float n = max(ceil(sqrt(.75 * 4. * sqrt(m))), 1.); parametricSegmentCount = min(uint(n), parametricSegmentCount); @@ -100,30 +110,30 @@ // *vertex* count is equal to the sum of polar and parametric *segment* counts. uint totalVertexCount = parametricSegmentCount + polarSegmentCount + joinSegmentCount - 1u; - float2x2 tangents = find_tangents(p); + float2x2 tangents = find_tangents(p0, p1, p2, p3); float theta = acos(cosine_between_vectors(tangents[0], tangents[1])); float radsPerPolarSegment = theta / float(polarSegmentCount); // Adjust sign of radsPerPolarSegment to match the direction the curve turns. // NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5). - // NOTE: F'(.5) x F''(.5) has the same sign as (p[2] - p[0]) x (p[3] - p[1]). - float turn = determinant(float2x2(p[2] - p[0], p[3] - p[1])); + // NOTE: F'(.5) x F''(.5) has the same sign as (p2 - p0) x (p3 - p1). + float turn = determinant(float2x2(p2 - p0, p3 - p1)); if (turn == .0) // This is the case for joins and cusps where points are co-located. turn = determinant(tangents); if (turn < .0) radsPerPolarSegment = -radsPerPolarSegment; - FLD(varyings, p0p1) = float4(p[0], p[1]); - FLD(varyings, p2p3) = float4(p[2], p[3]); - FLD(varyings, args) = float4(float(totalVertexCount) + coord.x - x1, // vertexIdx - float(totalVertexCount), // totalVertexCount - (joinSegmentCount << 10) | parametricSegmentCount, - radsPerPolarSegment); + v_p0p1 = float4(p0, p1); + v_p2p3 = float4(p2, p3); + v_args = float4(float(totalVertexCount) + coord.x - x1, // vertexIdx + float(totalVertexCount), // totalVertexCount + (joinSegmentCount << 10) | parametricSegmentCount, + radsPerPolarSegment); if (joinSegmentCount > 1u) { - float2x2 joinTangents = float2x2(tangents[1], attr_joinTangent.xy); + float2x2 joinTangents = float2x2(tangents[1], @a_joinTangent.xy); float joinTheta = acos(cosine_between_vectors(joinTangents[0], joinTangents[1])); float joinSpan = float(joinSegmentCount); - if ((outContourIDWithFlags & (JOIN_TYPE_MASK | EMULATED_STROKE_CAP_FLAG)) == + if ((contourIDWithFlags & (JOIN_TYPE_MASK | EMULATED_STROKE_CAP_FLAG)) == EMULATED_STROKE_CAP_FLAG) { // Round caps emulated as joins need to emit vertices at T=0 and T=1, unlike normal @@ -135,41 +145,50 @@ float radsPerJoinSegment = joinTheta / joinSpan; if (determinant(joinTangents) < .0) radsPerJoinSegment = -radsPerJoinSegment; - FLD(varyings, joinArgs).xy = attr_joinTangent.xy; - FLD(varyings, joinArgs).z = radsPerJoinSegment; + v_joinArgs.xy = @a_joinTangent.xy; + v_joinArgs.z = radsPerJoinSegment; } - FLD(varyings, contourIDWithFlags) = outContourIDWithFlags; + v_contourIDWithFlags = contourIDWithFlags; - POSITION.xy = coord * float2(2. / TESS_TEXTURE_WIDTH, uniforms.tessInverseViewportY) - 1.; - POSITION.zw = float2(0, 1); - EMIT_OFFSCREEN_VERTEX(varyings); + _pos.xy = coord * float2(2. / TESS_TEXTURE_WIDTH, uniforms.tessInverseViewportY) - 1.; + _pos.zw = float2(0, 1); + + VARYING_PACK(varyings, v_p0p1); + VARYING_PACK(varyings, v_p2p3); + VARYING_PACK(varyings, v_args); + VARYING_PACK(varyings, v_joinArgs); + VARYING_PACK(varyings, v_contourIDWithFlags); + EMIT_OFFSCREEN_VERTEX(varyings, _pos); } #endif #ifdef @FRAGMENT -FRAG_DATA_TYPE(uint4) -FRAG_DATA_MAIN( -#ifdef METAL - @tessellateFragmentMain, - Varyings varyings [[stage_in]] -#endif -) +FRAG_DATA_MAIN(uint4, @tessellateFragmentMain, Varyings, varyings) { - float4x2 p = make_float4x2(FLD(varyings, p0p1), FLD(varyings, p2p3)); - float2x2 tangents = find_tangents(p); + VARYING_UNPACK(varyings, v_p0p1, float4); + VARYING_UNPACK(varyings, v_p2p3, float4); + VARYING_UNPACK(varyings, v_args, float4); + VARYING_UNPACK(varyings, v_joinArgs, float3); + VARYING_UNPACK(varyings, v_contourIDWithFlags, uint); + + float2 p0 = v_p0p1.xy; + float2 p1 = v_p0p1.zw; + float2 p2 = v_p2p3.xy; + float2 p3 = v_p2p3.zw; + float2x2 tangents = find_tangents(p0, p1, p2, p3); // Colocate any padding vertices at T=0. - float vertexIdx = max(floor(FLD(varyings, args).x), .0); - float totalVertexCount = FLD(varyings, args).y; - uint joinSegmentCount_and_parametricSegmentCount = uint(FLD(varyings, args).z); + float vertexIdx = max(floor(v_args.x), .0); + float totalVertexCount = v_args.y; + uint joinSegmentCount_and_parametricSegmentCount = uint(v_args.z); float parametricSegmentCount = float(joinSegmentCount_and_parametricSegmentCount & 0x3ffu); float joinSegmentCount = float(joinSegmentCount_and_parametricSegmentCount >> 10); - float radsPerPolarSegment = FLD(varyings, args).w; - uint outContourIDWithFlags = FLD(varyings, contourIDWithFlags); + float radsPerPolarSegment = v_args.w; + uint contourIDWithFlags = v_contourIDWithFlags; // PLSRenderer sends down the first curve of each contour with the // "FIRST_VERTEX_OF_CONTOUR_FLAG" flag. But from here we need to only output this flag for the // first *vertex* of that first curve. if (vertexIdx != .0) - outContourIDWithFlags &= ~FIRST_VERTEX_OF_CONTOUR_FLAG; + contourIDWithFlags &= ~FIRST_VERTEX_OF_CONTOUR_FLAG; // mergedVertexID/mergedSegmentCount are relative to the sub-section of the instance this vertex // belongs to (either the curve section that consists of merged polar and parametric segments, @@ -181,27 +200,27 @@ if (mergedVertexID <= mergedSegmentCount) { // We do belong to the curve section. Clear out any stroke join flags. - outContourIDWithFlags &= ~JOIN_TYPE_MASK; + contourIDWithFlags &= ~JOIN_TYPE_MASK; } else { // We actually belong to the join section following the curve. Construct a point-cubic with // rotation. - p = float4x2(p[3], p[3], p[3], p[3]); - tangents = float2x2(tangents[1], FLD(varyings, joinArgs).xy /*joinTangent*/); + p0 = p1 = p2 = p3; + tangents = float2x2(tangents[1], v_joinArgs.xy /*joinTangent*/); parametricSegmentCount = 1.; mergedVertexID -= mergedSegmentCount; mergedSegmentCount = joinSegmentCount; - if ((outContourIDWithFlags & JOIN_TYPE_MASK) != 0u) + if ((contourIDWithFlags & JOIN_TYPE_MASK) != 0u) { // Miter or bevel join vertices snap to either tangents[0] or tangents[1], and get // adjusted in the shader that follows. if (mergedVertexID < 2.5) // With 5 join segments, this branch will see IDs: 1, 2, 3, 4. - outContourIDWithFlags |= JOIN_TANGENT_0_FLAG; + contourIDWithFlags |= JOIN_TANGENT_0_FLAG; if (mergedVertexID > 1.5 && mergedVertexID < 3.5) - outContourIDWithFlags |= JOIN_TANGENT_INNER_FLAG; + contourIDWithFlags |= JOIN_TANGENT_INNER_FLAG; } - else if ((outContourIDWithFlags & EMULATED_STROKE_CAP_FLAG) != 0u) + else if ((contourIDWithFlags & EMULATED_STROKE_CAP_FLAG) != 0u) { // Round caps emulated as joins need to emit vertices at T=0 and T=1, unlike normal // round joins. Preserve the same number of vertices (the CPU should have given us two @@ -211,29 +230,28 @@ mergedSegmentCount -= 2.; mergedVertexID--; } - radsPerPolarSegment = FLD(varyings, joinArgs).z; // radsPerJoinSegment. - outContourIDWithFlags |= radsPerPolarSegment < .0 ? LEFT_JOIN_FLAG : RIGHT_JOIN_FLAG; + radsPerPolarSegment = v_joinArgs.z; // radsPerJoinSegment. + contourIDWithFlags |= radsPerPolarSegment < .0 ? LEFT_JOIN_FLAG : RIGHT_JOIN_FLAG; } float2 tessCoord; - float theta; + float theta = .0; if (mergedVertexID == .0 || mergedVertexID == mergedSegmentCount || - (outContourIDWithFlags & JOIN_TYPE_MASK) != 0u) + (contourIDWithFlags & JOIN_TYPE_MASK) != 0u) { // Tessellated vertices at the beginning and end of the strip use exact endpoints and // tangents. This ensures crack-free seaming between instances. bool isTan0 = mergedVertexID < mergedSegmentCount * .5; - tessCoord = isTan0 ? p[0] : p[3]; + tessCoord = isTan0 ? p0 : p3; theta = atan2(isTan0 ? tangents[0] : tangents[1]); } - else if ((outContourIDWithFlags & RETROFITTED_TRIANGLE_FLAG) != 0u) + else if ((contourIDWithFlags & RETROFITTED_TRIANGLE_FLAG) != 0u) { // This cubic should actually be drawn as the single, non-AA triangle: [p0, p1, p3]. // This is used to squeeze in more rare triangles, like "breadcrumb" triangles from // self-intersections on interior triangulation, where it wouldn't be worth it to put them // in their own dedicated draw call. - tessCoord = p[1]; - theta = .0; + tessCoord = p1; } else { @@ -255,9 +273,9 @@ // |T^2| // Tangent_Direction(T) = dx,dy = |A 2B C| * |T | // |. . .| |1 | - float2 A, B, C = p[1] - p[0]; - float2 D = p[3] - p[0]; - float2 E = p[2] - p[1]; + float2 A, B, C = p1 - p0; + float2 D = p3 - p0; + float2 E = p2 - p1; B = E - C; A = -3. * E + D; // FIXME(crbug.com/800804,skbug.com/11268): Consider normalizing the exponents in A,B,C @@ -352,9 +370,9 @@ } // Evaluate the cubic at T. Use De Casteljau's for its accuracy and stability. - float2 ab = unchecked_mix(p[0], p[1], T); - float2 bc = unchecked_mix(p[1], p[2], T); - float2 cd = unchecked_mix(p[2], p[3], T); + float2 ab = unchecked_mix(p0, p1, T); + float2 bc = unchecked_mix(p1, p2, T); + float2 cd = unchecked_mix(p2, p3, T); float2 abc = unchecked_mix(ab, bc, T); float2 bcd = unchecked_mix(bc, cd, T); tessCoord = unchecked_mix(abc, bcd, T); @@ -365,6 +383,6 @@ theta = atan2(bcd - abc); } - EMIT_FRAG_DATA(uint4(floatBitsToUint(float3(tessCoord, theta)), outContourIDWithFlags)); + EMIT_FRAG_DATA(uint4(floatBitsToUint(float3(tessCoord, theta)), contourIDWithFlags)); } #endif