From 5da60f584c93dd2d8fdc7d46f08c625e891b0756 Mon Sep 17 00:00:00 2001 From: treeform Date: Tue, 26 May 2026 06:44:32 -0700 Subject: [PATCH 1/4] Use Shady shaders for OpenGL and DirectX --- src/silky/drawers/dx12.nim | 164 +++++++++----------------- src/silky/drawers/ogl.nim | 75 ++---------- src/silky/drawers/shader.nim | 57 +++++++++ tests/test_directx_shader_runtime.nim | 16 +++ tests/test_shader_codegen.nim | 36 ++++++ 5 files changed, 176 insertions(+), 172 deletions(-) create mode 100644 src/silky/drawers/shader.nim create mode 100644 tests/test_directx_shader_runtime.nim create mode 100644 tests/test_shader_codegen.nim diff --git a/src/silky/drawers/dx12.nim b/src/silky/drawers/dx12.nim index 6e1225a..fd40dba 100644 --- a/src/silky/drawers/dx12.nim +++ b/src/silky/drawers/dx12.nim @@ -1,9 +1,11 @@ when not defined(windows): {.error: "The Silky DirectX 12 backend requires Windows.".} -import - pixie, vmath, windy, - pkg/dx12, pkg/dx12/context +import + pixie, vmath, windy, + pkg/dx12, pkg/dx12/context, + silky/drawers/shader +from shady import toHLSL, shaderVertex, shaderFragment const InitialVertexCapacity = 4096 @@ -32,7 +34,7 @@ type vertexBufferPtr: pointer maxVertexCount: int viewportSize: IVec2 - clearColor: array[4, FLOAT] + clearColor: array[4, float32] layers*: array[2, seq[DrawerVertex]] currentLayer*: int layerStack*: seq[int] @@ -41,25 +43,7 @@ proc clampViewport(size: IVec2): IVec2 = ## Clamps the viewport to valid swap-chain dimensions. ivec2(max(1'i32, size.x), max(1'i32, size.y)) -proc normalizeVertices( - vertices: var seq[DrawerVertex], - viewportSize: IVec2, - atlasSize: Vec2 -) = - ## Converts queued pixel-space vertices to clip-space and normalized UVs. - let - width = max(1.0'f, viewportSize.x.float32) - height = max(1.0'f, viewportSize.y.float32) - for i in 0 ..< vertices.len: - let p = vertices[i].pos - vertices[i].pos = vec2( - (p.x / width) * 2.0'f - 1.0'f, - 1.0'f - (p.y / height) * 2.0'f - ) - vertices[i].uv = vertices[i].uv / atlasSize - vertices[i].maskUv = vertices[i].maskUv / atlasSize - -proc createVertexBuffer(state: Drawer, maxVertexCount: int) = +proc createVertexBuffer(state: Drawer, maxVertexCount: int) = ## Creates or replaces the persistently mapped upload vertex buffer. if state.vertexBuffer != nil: state.vertexBuffer.unmap(0, nil) @@ -67,7 +51,7 @@ proc createVertexBuffer(state: Drawer, maxVertexCount: int) = state.vertexBuffer = nil state.vertexBufferPtr = nil - let vertexBufferSize = UINT64(maxVertexCount * sizeof(DrawerVertex)) + let vertexBufferSize = uint64(maxVertexCount * sizeof(DrawerVertex)) var bufferDesc: D3D12_RESOURCE_DESC zeroMem(addr bufferDesc, sizeof(bufferDesc)) @@ -139,9 +123,9 @@ proc uploadTexture(state: Drawer, image: Image) = ) var footprint = D3D12_PLACED_SUBRESOURCE_FOOTPRINT() - var numRows: uint32 - var rowSize: UINT64 - var totalBytes: UINT64 + var numRows: uint32 + var rowSize: uint64 + var totalBytes: uint64 state.ctx.device.getCopyableFootprints( addr texDesc, 0, @@ -271,72 +255,14 @@ proc uploadTexture(state: Drawer, image: Image) = srvCpuHandle ) -proc initRenderer(state: Drawer, image: Image, size: IVec2) = - ## Creates the DX12 pipeline, upload buffers, and atlas texture. - const vertexShaderSrc = """ -struct VSInput { - float2 pos : POSITION; - float2 uv : TEXCOORD0; - float4 color : COLOR0; - float2 clipPos : TEXCOORD1; - float2 clipSize : TEXCOORD2; - float2 maskUv : TEXCOORD3; -}; - -struct PSInput { - float4 pos : SV_POSITION; - float2 uv : TEXCOORD0; - float4 color : COLOR0; - float2 clipPos : TEXCOORD1; - float2 clipSize : TEXCOORD2; - float2 maskUv : TEXCOORD3; -}; - -PSInput VSMain(VSInput input) { - PSInput output; - output.pos = float4(input.pos, 0.0f, 1.0f); - output.uv = input.uv; - output.color = input.color; - output.clipPos = input.clipPos; - output.clipSize = input.clipSize; - output.maskUv = input.maskUv; - return output; -} -""" - - const pixelShaderSrc = """ -Texture2D tex0 : register(t0); -SamplerState samp0 : register(s0); - -struct PSInput { - float4 pos : SV_POSITION; - float2 uv : TEXCOORD0; - float4 color : COLOR0; - float2 clipPos : TEXCOORD1; - float2 clipSize : TEXCOORD2; - float2 maskUv : TEXCOORD3; -}; - -float4 PSMain(PSInput input) : SV_TARGET { - if (input.pos.x < input.clipPos.x || - input.pos.y < input.clipPos.y || - input.pos.x > input.clipPos.x + input.clipSize.x || - input.pos.y > input.clipPos.y + input.clipSize.y) { - discard; - } - float4 base = tex0.Sample(samp0, input.uv); - if (input.maskUv.x >= 0.0) { - float maskR = tex0.Sample(samp0, input.maskUv).r; - return float4(base.rgb * lerp(float3(1,1,1), input.color.rgb, maskR), base.a * input.color.a); - } - return base * input.color; -} -""" - - let - safeSize = clampViewport(size) - vsBlob = compileShader(vertexShaderSrc, "VSMain", "vs_5_0") - psBlob = compileShader(pixelShaderSrc, "PSMain", "ps_5_0") +proc initRenderer(state: Drawer, image: Image, size: IVec2) = + ## Creates the DX12 pipeline, upload buffers, and atlas texture. + let + safeSize = clampViewport(size) + vertexShaderSrc = toHLSL(silkyVert, shaderVertex) + pixelShaderSrc = toHLSL(silkyFrag, shaderFragment) + vsBlob = compileShader(vertexShaderSrc, "VSMain", "vs_5_0") + psBlob = compileShader(pixelShaderSrc, "PSMain", "ps_5_0") var range = D3D12_DESCRIPTOR_RANGE( RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV, @@ -347,10 +273,21 @@ float4 PSMain(PSInput input) : SV_TARGET { D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND ) - var rootParams = [ - D3D12_ROOT_PARAMETER( - ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, - data: D3D12_ROOT_PARAMETER_UNION( + var rootParams = [ + D3D12_ROOT_PARAMETER( + ParameterType: D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, + data: D3D12_ROOT_PARAMETER_UNION( + Constants: D3D12_ROOT_CONSTANTS( + ShaderRegister: 0, + RegisterSpace: 0, + Num32BitValues: 4 + ) + ), + ShaderVisibility: D3D12_SHADER_VISIBILITY_VERTEX + ), + D3D12_ROOT_PARAMETER( + ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, + data: D3D12_ROOT_PARAMETER_UNION( DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE( NumDescriptorRanges: 1, pDescriptorRanges: addr range @@ -565,7 +502,7 @@ proc ensureVertexCapacity(state: Drawer, vertexCount: int) = newCapacity *= 2 state.createVertexBuffer(newCapacity) -proc recordDraw(state: Drawer, vertexCount: int) = +proc recordDraw(state: Drawer, vertexCount: int, atlasSize: Vec2) = ## Records the DX12 draw pass for the current frame. state.ctx.commandAllocator.reset() state.ctx.commandList.reset(state.ctx.commandAllocator, state.pipelineState) @@ -598,12 +535,24 @@ proc recordDraw(state: Drawer, vertexCount: int) = nil ) - var heaps = [state.srvHeap] - state.ctx.commandList.setDescriptorHeaps(1, addr heaps[0]) - state.ctx.commandList.setGraphicsRootDescriptorTable( - 0, - state.srvHandleGpu - ) + var heaps = [state.srvHeap] + state.ctx.commandList.setDescriptorHeaps(1, addr heaps[0]) + var shaderUniforms = [ + state.viewportSize.x.float32, + state.viewportSize.y.float32, + atlasSize.x, + atlasSize.y + ] + state.ctx.commandList.setGraphicsRoot32BitConstants( + 0, + uint32(shaderUniforms.len), + unsafeAddr shaderUniforms[0], + 0 + ) + state.ctx.commandList.setGraphicsRootDescriptorTable( + 1, + state.srvHandleGpu + ) state.ctx.commandList.iaSetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST ) @@ -638,16 +587,15 @@ proc endFrame*( let quadsArr = cast[ptr UncheckedArray[DrawerVertex]](quads) for i in 0 ..< quadCount: vertices.add(quadsArr[i]) - vertices.normalizeVertices(drawer.viewportSize, atlasSize) - - if vertexCount > 0: + + if vertexCount > 0: copyMem( drawer.vertexBufferPtr, unsafeAddr vertices[0], vertexCount * sizeof(DrawerVertex) ) - drawer.recordDraw(vertexCount) + drawer.recordDraw(vertexCount, atlasSize) drawer.ctx.executeFrame( if drawer.window != nil: drawer.window.vsync else: true ) diff --git a/src/silky/drawers/ogl.nim b/src/silky/drawers/ogl.nim index 25b40b3..8b652c2 100644 --- a/src/silky/drawers/ogl.nim +++ b/src/silky/drawers/ogl.nim @@ -1,6 +1,7 @@ -import - std/[strformat, strutils], - pixie, opengl, shady, vmath, windy +import + std/[strformat, strutils], + pixie, opengl, shady, vmath, windy, + silky/drawers/shader type BufferKind* = enum @@ -50,12 +51,7 @@ type currentLayer*: int layerStack*: seq[int] -var - mvp: Uniform[Mat4] - atlasSize: Uniform[Vec2] - atlasSampler: Uniform[Sampler2D] - -func size(componentType: GLenum): Positive = +func size(componentType: GLenum): Positive = ## Returns the byte size of a GL component type. case componentType: of cGL_BYTE, cGL_UNSIGNED_BYTE: @@ -467,55 +463,7 @@ proc bindUniforms*(shader: Shader) = uniform.changed = false -proc silkyVert( - pos: Vec2, - uv: Vec2, - color: ColorRGBX, - clipPos: Vec2, - clipSize: Vec2, - maskUv: Vec2, - fragmentUv: var Vec2, - fragmentColor: var Vec4, - fragmentClipPos: var Vec2, - fragmentClipSize: var Vec2, - fragmentPos: var Vec2, - fragmentMaskUv: var Vec2 -) = - ## Vertex shader for Silky's OpenGL drawer. - gl_Position = mvp * vec4(pos.x, pos.y, 0.0, 1.0) - fragmentUv = uv / atlasSize - fragmentColor = color.vec4 - fragmentClipPos = clipPos - fragmentClipSize = clipSize - fragmentPos = pos - fragmentMaskUv = maskUv / atlasSize - -proc silkyFrag( - fragmentUv: Vec2, - fragmentColor: Vec4, - fragmentClipPos: Vec2, - fragmentClipSize: Vec2, - fragmentPos: Vec2, - fragmentMaskUv: Vec2, - fragColor: var Vec4 -) = - ## Fragment shader for Silky's OpenGL drawer. - if fragmentPos.x < fragmentClipPos.x or - fragmentPos.y < fragmentClipPos.y or - fragmentPos.x > fragmentClipPos.x + fragmentClipSize.x or - fragmentPos.y > fragmentClipPos.y + fragmentClipSize.y: - discardFragment() - elif fragmentMaskUv.x >= 0.0: - let base = texture(atlasSampler, fragmentUv) - let maskR = texture(atlasSampler, fragmentMaskUv).r - fragColor = vec4( - base.rgb * mix(vec3(1.0), fragmentColor.rgb, maskR), - base.a * fragmentColor.a - ) - else: - fragColor = texture(atlasSampler, fragmentUv) * fragmentColor - -proc newDrawer*(window: Window, image: Image): Drawer = +proc newDrawer*(window: Window, image: Image): Drawer = ## Creates a new OpenGL drawer and eagerly uploads its resources. discard window result = Drawer() @@ -670,12 +618,11 @@ proc endFrame*( GL_STREAM_DRAW ) - glUseProgram(drawer.shader.programId) - mvp = ortho(0.0'f, size.x, size.y, 0.0'f, -1000.0, 1000.0) - drawer.shader.setUniform("mvp", mvp) - drawer.shader.setUniform( - "atlasSize", - vec2(image.width.float32, image.height.float32) + glUseProgram(drawer.shader.programId) + drawer.shader.setUniform("viewportSize", size) + drawer.shader.setUniform( + "atlasSize", + vec2(image.width.float32, image.height.float32) ) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, drawer.atlasTexture) diff --git a/src/silky/drawers/shader.nim b/src/silky/drawers/shader.nim new file mode 100644 index 0000000..4dd4a58 --- /dev/null +++ b/src/silky/drawers/shader.nim @@ -0,0 +1,57 @@ +import pixie, shady, vmath + +var + viewportSize*: Uniform[Vec2] + atlasSize*: Uniform[Vec2] + atlasSampler*: Uniform[Sampler2D] + +proc silkyVert*( + pos: Vec2, + uv: Vec2, + color: ColorRGBX, + clipPos: Vec2, + clipSize: Vec2, + maskUv: Vec2, + gl_Position: var Vec4, + fragmentUv: var Vec2, + fragmentColor: var Vec4, + fragmentClipPos: var Vec2, + fragmentClipSize: var Vec2, + fragmentPos: var Vec2, + fragmentMaskUv: var Vec2 +) = + let ndc = vec2( + pos.x / viewportSize.x * 2.0'f32 - 1.0'f32, + 1.0'f32 - pos.y / viewportSize.y * 2.0'f32 + ) + gl_Position = vec4(ndc.x, ndc.y, 0.0, 1.0) + fragmentUv = uv / atlasSize + fragmentColor = vec4(color) + fragmentClipPos = clipPos + fragmentClipSize = clipSize + fragmentPos = pos + fragmentMaskUv = maskUv / atlasSize + +proc silkyFrag*( + fragmentUv: Vec2, + fragmentColor: Vec4, + fragmentClipPos: Vec2, + fragmentClipSize: Vec2, + fragmentPos: Vec2, + fragmentMaskUv: Vec2, + fragColor: var Vec4 +) = + if fragmentPos.x < fragmentClipPos.x or + fragmentPos.y < fragmentClipPos.y or + fragmentPos.x > fragmentClipPos.x + fragmentClipSize.x or + fragmentPos.y > fragmentClipPos.y + fragmentClipSize.y: + discardFragment() + elif fragmentMaskUv.x >= 0.0: + let base = texture(atlasSampler, fragmentUv) + let maskR = texture(atlasSampler, fragmentMaskUv).r + fragColor = vec4( + base.rgb * mix(vec3(1.0, 1.0, 1.0), fragmentColor.rgb, maskR), + base.a * fragmentColor.a + ) + else: + fragColor = texture(atlasSampler, fragmentUv) * fragmentColor diff --git a/tests/test_directx_shader_runtime.nim b/tests/test_directx_shader_runtime.nim new file mode 100644 index 0000000..f9ec007 --- /dev/null +++ b/tests/test_directx_shader_runtime.nim @@ -0,0 +1,16 @@ +when defined(windows) and defined(useDirectX) and defined(shadyRunDx12): + import silky, vmath + + let window = newWindow( + title = "Silky Shady DirectX shader test", + size = ivec2(64, 64), + visible = false + ) + let atlasBuilder = newAtlasBuilder(32, 1) + + discard newSilky(window, atlasBuilder.atlasImage, atlasBuilder.atlas) + echo "Silky DirectX Shady shader runtime test passed" +elif defined(windows) and defined(useDirectX): + echo "Silky DirectX Shady shader runtime test skipped; run with -d:shadyRunDx12" +else: + echo "Silky DirectX Shady shader runtime test skipped; Windows DirectX is required" diff --git a/tests/test_shader_codegen.nim b/tests/test_shader_codegen.nim new file mode 100644 index 0000000..f001069 --- /dev/null +++ b/tests/test_shader_codegen.nim @@ -0,0 +1,36 @@ +import strutils, shady, silky/drawers/shader + +block: + let vertex = toShader(silkyVert, glsl3Desktop, shaderVertex) + doAssert "uniform vec2 viewportSize;" in vertex + doAssert "uniform vec2 atlasSize;" in vertex + doAssert "in vec2 pos;" in vertex + doAssert "in vec2 maskUv;" in vertex + doAssert "out vec2 fragmentUv;" in vertex + doAssert "out vec2 fragmentMaskUv;" in vertex + + let fragment = toShader(silkyFrag, glsl3Desktop, shaderFragment) + doAssert "uniform sampler2D atlasSampler;" in fragment + doAssert "discard" in fragment + doAssert "texture(atlasSampler, fragmentUv)" in fragment + doAssert "(texture(atlasSampler, fragmentMaskUv)).r" in fragment + doAssert "mix(vec3(1.0, 1.0, 1.0), fragmentColor.rgb, maskR)" in fragment + +block: + let vertex = toHLSL(silkyVert, shaderVertex) + doAssert "cbuffer ShadyUniforms : register(b0)" in vertex + doAssert "float2 viewportSize;" in vertex + doAssert "float2 atlasSize;" in vertex + doAssert "float2 pos : POSITION0" in vertex + doAssert "float2 maskUv : TEXCOORD3" in vertex + doAssert "float4 pos : SV_POSITION;" in vertex + + let fragment = toHLSL(silkyFrag, shaderFragment) + doAssert "Texture2D atlasSampler : register(t0);" in fragment + doAssert "SamplerState atlasSamplerSampler : register(s0);" in fragment + doAssert "float4 gl_FragCoord : SV_POSITION" in fragment + doAssert "atlasSampler.Sample(atlasSamplerSampler, fragmentUv)" in fragment + doAssert "(atlasSampler.Sample(atlasSamplerSampler, fragmentMaskUv)).r" in fragment + doAssert "lerp(float3(1.0, 1.0, 1.0), fragmentColor.rgb, maskR)" in fragment + +echo "Silky Shady shader codegen tests passed" From 96d619d24dee063f340b20757d47b5c13c788469 Mon Sep 17 00:00:00 2001 From: treeform Date: Tue, 26 May 2026 07:54:01 -0700 Subject: [PATCH 2/4] Add source and binary shader modes --- src/silky/drawers/dx12.nim | 82 +++++++++++++---- src/silky/drawers/shaders/silky.frag | 52 +++++------ src/silky/drawers/shaders/silky.frag.hlsl | 28 ++++++ src/silky/drawers/shaders/silky.frag.spv | Bin 2648 -> 2660 bytes src/silky/drawers/shaders/silky.ps.cso | Bin 0 -> 4968 bytes src/silky/drawers/shaders/silky.vert | 67 +++++++------- src/silky/drawers/shaders/silky.vert.hlsl | 52 +++++++++++ src/silky/drawers/shaders/silky.vert.spv | Bin 2176 -> 2536 bytes src/silky/drawers/shaders/silky.vs.cso | Bin 0 -> 5112 bytes src/silky/drawers/vk14.nim | 104 +++++++++++++--------- tests/test_shader_codegen.nim | 17 ++++ 11 files changed, 279 insertions(+), 123 deletions(-) create mode 100644 src/silky/drawers/shaders/silky.frag.hlsl create mode 100644 src/silky/drawers/shaders/silky.ps.cso create mode 100644 src/silky/drawers/shaders/silky.vert.hlsl create mode 100644 src/silky/drawers/shaders/silky.vs.cso diff --git a/src/silky/drawers/dx12.nim b/src/silky/drawers/dx12.nim index fd40dba..763ca8b 100644 --- a/src/silky/drawers/dx12.nim +++ b/src/silky/drawers/dx12.nim @@ -3,12 +3,22 @@ when not defined(windows): import pixie, vmath, windy, - pkg/dx12, pkg/dx12/context, - silky/drawers/shader -from shady import toHLSL, shaderVertex, shaderFragment - -const - InitialVertexCapacity = 4096 + pkg/dx12, pkg/dx12/context + +when not defined(shadyBinaryShaders): + import std/os, silky/drawers/shader + from shady import compileHlslShader, toHLSL, shaderVertex, shaderFragment + +const + InitialVertexCapacity = 4096 + +when not defined(shadyBinaryShaders): + const + ShaderDir = currentSourcePath().parentDir / "shaders" + VertexHlslPath = ShaderDir / "silky.vert.hlsl" + PixelHlslPath = ShaderDir / "silky.frag.hlsl" + VertexCsoPath = ShaderDir / "silky.vs.cso" + PixelCsoPath = ShaderDir / "silky.ps.cso" type DrawerVertex* {.packed.} = object @@ -39,9 +49,15 @@ type currentLayer*: int layerStack*: seq[int] -proc clampViewport(size: IVec2): IVec2 = - ## Clamps the viewport to valid swap-chain dimensions. - ivec2(max(1'i32, size.x), max(1'i32, size.y)) +proc clampViewport(size: IVec2): IVec2 = + ## Clamps the viewport to valid swap-chain dimensions. + ivec2(max(1'i32, size.x), max(1'i32, size.y)) + +proc shaderBytecode(code: string): D3D12_SHADER_BYTECODE = + D3D12_SHADER_BYTECODE( + pShaderBytecode: unsafeAddr code[0], + BytecodeLength: csize_t(code.len) + ) proc createVertexBuffer(state: Drawer, maxVertexCount: int) = ## Creates or replaces the persistently mapped upload vertex buffer. @@ -257,12 +273,41 @@ proc uploadTexture(state: Drawer, image: Image) = proc initRenderer(state: Drawer, image: Image, size: IVec2) = ## Creates the DX12 pipeline, upload buffers, and atlas texture. + when defined(shadyBinaryShaders): + const + vertexShaderCode = staticRead("shaders/silky.vs.cso") + pixelShaderCode = staticRead("shaders/silky.ps.cso") + else: + const + vertexShaderSrc = toHLSL(silkyVert, shaderVertex) + pixelShaderSrc = toHLSL(silkyFrag, shaderFragment) + vertexShaderCode = compileHlslShader( + vertexShaderSrc, + VertexHlslPath, + VertexCsoPath, + "VSMain", + "vs_6_0" + ) + pixelShaderCode = compileHlslShader( + pixelShaderSrc, + PixelHlslPath, + PixelCsoPath, + "PSMain", + "ps_6_0" + ) let safeSize = clampViewport(size) - vertexShaderSrc = toHLSL(silkyVert, shaderVertex) - pixelShaderSrc = toHLSL(silkyFrag, shaderFragment) - vsBlob = compileShader(vertexShaderSrc, "VSMain", "vs_5_0") - psBlob = compileShader(pixelShaderSrc, "PSMain", "ps_5_0") + + when defined(shadyBinaryShaders): + let + vsBytecode = shaderBytecode(vertexShaderCode) + psBytecode = shaderBytecode(pixelShaderCode) + else: + let + vsBlob = compileShader(vertexShaderSrc, "VSMain", "vs_5_0") + psBlob = compileShader(pixelShaderSrc, "PSMain", "ps_5_0") + vsBytecode = shaderBytecode(vsBlob) + psBytecode = shaderBytecode(psBlob) var range = D3D12_DESCRIPTOR_RANGE( RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV, @@ -409,9 +454,9 @@ proc initRenderer(state: Drawer, image: Image, size: IVec2) = ) var psoDesc = D3D12_GRAPHICS_PIPELINE_STATE_DESC( - pRootSignature: state.rootSignature, - VS: shaderBytecode(vsBlob), - PS: shaderBytecode(psBlob), + pRootSignature: state.rootSignature, + VS: vsBytecode, + PS: psBytecode, StreamOutput: D3D12_STREAM_OUTPUT_DESC(), BlendState: blendDesc, SampleMask: D3D12_DEFAULT_SAMPLE_MASK, @@ -456,8 +501,9 @@ proc initRenderer(state: Drawer, image: Image, size: IVec2) = addr psoDesc ) - release(vsBlob) - release(psBlob) + when not defined(shadyBinaryShaders): + release(vsBlob) + release(psBlob) state.viewportSize = safeSize state.createVertexBuffer(InitialVertexCapacity) diff --git a/src/silky/drawers/shaders/silky.frag b/src/silky/drawers/shaders/silky.frag index 46ee489..560ef28 100644 --- a/src/silky/drawers/shaders/silky.frag +++ b/src/silky/drawers/shaders/silky.frag @@ -1,28 +1,24 @@ -#version 450 - -layout(set = 0, binding = 0) uniform sampler2D tex0; - -layout(location = 0) in vec2 fragUv; -layout(location = 1) in vec4 fragColor; -layout(location = 2) in vec2 fragClipPos; -layout(location = 3) in vec2 fragClipSize; -layout(location = 4) in vec2 fragPos; -layout(location = 5) in vec2 fragMaskUv; - -layout(location = 0) out vec4 outColor; - -void main() { - if (fragPos.x < fragClipPos.x || - fragPos.y < fragClipPos.y || - fragPos.x > fragClipPos.x + fragClipSize.x || - fragPos.y > fragClipPos.y + fragClipSize.y) { - discard; - } - vec4 base = texture(tex0, fragUv); - if (fragMaskUv.x >= 0.0) { - float maskR = texture(tex0, fragMaskUv).r; - outColor = vec4(base.rgb * mix(vec3(1.0), fragColor.rgb, maskR), base.a * fragColor.a); - } else { - outColor = base * fragColor; - } -} +#version 450 +// from silkyFrag + +layout(set = 0, binding = 0) uniform sampler2D atlasSampler; + +layout(location = 0) in vec2 fragmentUv; +layout(location = 1) in vec4 fragmentColor; +layout(location = 2) in vec2 fragmentClipPos; +layout(location = 3) in vec2 fragmentClipSize; +layout(location = 4) in vec2 fragmentPos; +layout(location = 5) in vec2 fragmentMaskUv; +layout(location = 0) out vec4 fragColor; + +void main() { + if ((((fragmentPos.x < fragmentClipPos.x) || (fragmentPos.y < fragmentClipPos.y)) || (fragmentClipPos.x + fragmentClipSize.x < fragmentPos.x)) || (fragmentClipPos.y + fragmentClipSize.y < fragmentPos.y)) { + discard; + } else if (0.0 <= float(fragmentMaskUv.x)) { + vec4 base = texture(atlasSampler, fragmentUv); + float maskR = (texture(atlasSampler, fragmentMaskUv)).r; + fragColor = vec4(base.rgb * mix(vec3(1.0, 1.0, 1.0), fragmentColor.rgb, maskR), base.a * fragmentColor.a); + } else { + fragColor = texture(atlasSampler, fragmentUv) * fragmentColor; + } +} diff --git a/src/silky/drawers/shaders/silky.frag.hlsl b/src/silky/drawers/shaders/silky.frag.hlsl new file mode 100644 index 0000000..c3ade02 --- /dev/null +++ b/src/silky/drawers/shaders/silky.frag.hlsl @@ -0,0 +1,28 @@ +// target hlsl dx12 +// from silkyFrag + +Texture2D atlasSampler : register(t0); +SamplerState atlasSamplerSampler : register(s0); + + +float4 PSMain( + float4 gl_FragCoord : SV_POSITION, + float2 fragmentUv : TEXCOORD0, + float4 fragmentColor : COLOR0, + float2 fragmentClipPos : TEXCOORD1, + float2 fragmentClipSize : TEXCOORD2, + float2 fragmentPos : TEXCOORD3, + float2 fragmentMaskUv : TEXCOORD4 +) : SV_TARGET { + float4 fragColor = float4(0.0, 0.0, 0.0, 0.0); + if ((((fragmentPos.x < fragmentClipPos.x) || (fragmentPos.y < fragmentClipPos.y)) || (fragmentClipPos.x + fragmentClipSize.x < fragmentPos.x)) || (fragmentClipPos.y + fragmentClipSize.y < fragmentPos.y)) { + discard; + } else if (0.0 <= float(fragmentMaskUv.x)) { + float4 base = atlasSampler.Sample(atlasSamplerSampler, fragmentUv); + float maskR = (atlasSampler.Sample(atlasSamplerSampler, fragmentMaskUv)).r; + fragColor = float4(base.rgb * lerp(float3(1.0, 1.0, 1.0), fragmentColor.rgb, maskR), base.a * fragmentColor.a); + } else { + fragColor = atlasSampler.Sample(atlasSamplerSampler, fragmentUv) * fragmentColor; + } + return fragColor; +} diff --git a/src/silky/drawers/shaders/silky.frag.spv b/src/silky/drawers/shaders/silky.frag.spv index b669e53549ebd4f39fc14a47e00226839705b87e..6afcc4ec01631a2511d0a3771639f8dff6811854 100644 GIT binary patch literal 2660 zcmZ9M*HRQg6oyBTpduK+gkb@*h&hxB3Md#rQNWxQMMZZ3S;`Bq^ul-X5xnrqcd`tn zRes;>9BYcM`uCjk_ley-TU*oBkR;2JRY_yAkW|;&qy{E|t54@e_yj zXijQVM}1n7rerx%l#4U7Zma}rKo>X%u7U~h0IVTaUy7pDQYlHkk*E$fpQ8@OJTDfC zFU!-ji(_*YnEEttaD%0p`6}O#*2?qaGw-JfPh;#M_C|}9HXzPLIPr>_6fo_BvddT(^+=IG;X)bEP>0?bUfBF8E=lbgu2dd9^(&ZO=m6 zbJO;ewe4TKF0<{k1GwDR?o6$!ZNK`v+_$&9ZC~2n-+HQAw7>Dyr%-# zNS;Qdw*Cd|6-aZ)pUrG%lWW6Xg)9Kqfy{2InrZxMbbZYk@t%cT_;1YY*!OH=zZwU@ ziha+@efu3i>f<@v?_g%jZ>IZ8nXP|*h275lLu}}0{BY(ZXFYAt-WBydd*k&VMOt5I zk0ZTP>+7%W-N@PRBvM~(=U2ZAlH|krZf?Z=dq6H=-uu|rjJ(?3v0R+v6uNm&@``GZ zo{3BDCyZ~)o(7SA7uMEx2H(1NKVIHhGw`0h%U*okYXS1%vliXE%=@gvmj6N?-`9Fz zocw3<$h89bA9S_>>H5u{d>fFnU-hW93Eh5tE78wpboubvf^I)~pLT5d=w~asaq_>Y z5w(=xnbQ8Y0sHg3^tX@b-EYc$doLhe_Ab8z6o9>}M_(Q2OV6njPCk5gqIZG(S?P7c|DSv-`*z#YqZ{a-pSs-tnex%Df z?p**SU~YBa`4I97@IOeq5qS+<1$W3hfE)q($oG-gZ(A*xZ{$GXvBXl|8%KQ7)9|LQSW&ep! zGEQDJWp$sT8|%CBf787Beqs*$Dgt|Z3cRaPZ{Ftxw!FEG zc?pb@k8^v4ZcSHkGw5>m8QklP^NhyGHh_EsjMv|N^LzFaNcVil{Yc-3?;%WaH82h4!2;OHT5~xRZYcW>O zgZ1Xaxo$6w@~1ZAsZM)sz>O7JgPU*v$VrLJk#rGzzuEggt`hfK-xv4by!E;6&J*)T zi&~9mn!QH@ZY0f+vqJnC)*SDm`?x>V?R3|13(mVK&d+2m>wXR1dW_X=?421O%dy=s zu3`@ZN5m6@u!z;_dWZAVSmV(gtE)V(CS&`&hf_UwqUhQ{!(Ny%xczYUvjPsV?-s1F z`4!?9uC`~x|TdGE$~^?ZnRt@o&& z3ptiQh`LKTHorU~K16+&nJ~}wS8|e^=NVTs7w3D|u8-KeHde1%t^GY$vgDhP=~QK_|A>5l4Z}f1N&?*FOlQi4j>;nyYTI0IcGPq{4@6PeeD6RlmCT% z-d{F`F%i6z4~$1OZe*Xt%RO~`0|nSGQN7sIfsblL(ePtu9N?ZGvX}m z?^LP2SAqJxFZ0zA+Wn@SS9=BPP`mu=paRscAG#*+H{Ru8IQhsqg1_-DkHX1E&T)Ld zjq-h;AeR3Lwf5)@;5zx|xd(6Jd!BRFT*0bQ8#TX$Z(ZJl`Rb3FPvSdo&2M8J)+~Pt z$hn_>Xr090*spisFa23nw2r@8Mf>xu@?F%SX*;@LeY#HJ`=zJm;)AG-{*f zbNJR(0p_dU8qZ@L)@aTLK+YQV|KUs2m-8+9TXGdtbL?;B6u1WN(gVNQkAOMympQ}l zZ@>+{w~vXZLAkdzV)y;8E#`g&z!tt6Bq)K|0Vtci2Sec<%FZ0?>qh)c;;OG zP5dTv@?t!n+rW3N@9G|KUvqz^4s|U8wVCIes$=g1wKswKJZ}le$Gcm`S8q9|MJ(^$ zu6Y1lCm;9r5PuOkg8LSK5vVh`cILdJId=0-zXPr}-+A}?c>=&wc+z;d$^MWsJl% diff --git a/src/silky/drawers/shaders/silky.ps.cso b/src/silky/drawers/shaders/silky.ps.cso new file mode 100644 index 0000000000000000000000000000000000000000..61896d78a1f9ac060450cf459c6e6416f9a8a562 GIT binary patch literal 4968 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 literal 0 HcmV?d00001 diff --git a/src/silky/drawers/shaders/silky.vert b/src/silky/drawers/shaders/silky.vert index 7ecff2b..4d81392 100644 --- a/src/silky/drawers/shaders/silky.vert +++ b/src/silky/drawers/shaders/silky.vert @@ -1,33 +1,34 @@ -#version 450 - -layout(location = 0) in vec2 inPos; -layout(location = 1) in vec2 inUv; -layout(location = 2) in vec4 inColor; -layout(location = 3) in vec2 inClipPos; -layout(location = 4) in vec2 inClipSize; -layout(location = 5) in vec2 inMaskUv; - -layout(location = 0) out vec2 fragUv; -layout(location = 1) out vec4 fragColor; -layout(location = 2) out vec2 fragClipPos; -layout(location = 3) out vec2 fragClipSize; -layout(location = 4) out vec2 fragPos; -layout(location = 5) out vec2 fragMaskUv; - -layout(push_constant) uniform PushConstants { - vec2 viewportSize; -} pc; - -void main() { - gl_Position = vec4(inPos, 0.0, 1.0); - fragUv = inUv; - fragColor = inColor; - fragClipPos = inClipPos; - fragClipSize = inClipSize; - fragMaskUv = inMaskUv; - // Recover pixel position from Vulkan NDC (-1 top, +1 bottom) - fragPos = vec2( - (inPos.x * 0.5 + 0.5) * pc.viewportSize.x, - (inPos.y * 0.5 + 0.5) * pc.viewportSize.y - ); -} +#version 450 +// from silkyVert + +layout(push_constant) uniform ShadyPushConstants { + vec2 viewportSize; + vec2 atlasSize; +} shadyPushConstants; +#define viewportSize shadyPushConstants.viewportSize +#define atlasSize shadyPushConstants.atlasSize + +layout(location = 0) in vec2 pos; +layout(location = 1) in vec2 uv; +layout(location = 2) in vec4 color; +layout(location = 3) in vec2 clipPos; +layout(location = 4) in vec2 clipSize; +layout(location = 5) in vec2 maskUv; +layout(location = 0) out vec2 fragmentUv; +layout(location = 1) out vec4 fragmentColor; +layout(location = 2) out vec2 fragmentClipPos; +layout(location = 3) out vec2 fragmentClipSize; +layout(location = 4) out vec2 fragmentPos; +layout(location = 5) out vec2 fragmentMaskUv; + +void main() { + vec2 ndc = vec2((pos.x / viewportSize.x) * 2.0 - 1.0, 1.0 - (pos.y / viewportSize.y) * 2.0); + gl_Position = vec4(ndc.x, ndc.y, 0.0, 1.0); + fragmentUv = uv / atlasSize; + fragmentColor = vec4(color); + fragmentClipPos = clipPos; + fragmentClipSize = clipSize; + fragmentPos = pos; + fragmentMaskUv = maskUv / atlasSize; + gl_Position.y = -gl_Position.y; +} diff --git a/src/silky/drawers/shaders/silky.vert.hlsl b/src/silky/drawers/shaders/silky.vert.hlsl new file mode 100644 index 0000000..b9ebe05 --- /dev/null +++ b/src/silky/drawers/shaders/silky.vert.hlsl @@ -0,0 +1,52 @@ +// target hlsl dx12 +// from silkyVert + +cbuffer ShadyUniforms : register(b0) { + float2 viewportSize; + float2 atlasSize; +}; + + +struct VSOutput { + float4 pos : SV_POSITION; + float2 fragmentUv : TEXCOORD0; + float4 fragmentColor : COLOR0; + float2 fragmentClipPos : TEXCOORD1; + float2 fragmentClipSize : TEXCOORD2; + float2 fragmentPos : TEXCOORD3; + float2 fragmentMaskUv : TEXCOORD4; +}; + +VSOutput VSMain( + float2 pos : POSITION0, + float2 uv : TEXCOORD0, + float4 color : COLOR0, + float2 clipPos : TEXCOORD1, + float2 clipSize : TEXCOORD2, + float2 maskUv : TEXCOORD3 +) { + VSOutput output; + float4 gl_Position = float4(0.0, 0.0, 0.0, 0.0); + float2 fragmentUv = float2(0.0, 0.0); + float4 fragmentColor = float4(0.0, 0.0, 0.0, 0.0); + float2 fragmentClipPos = float2(0.0, 0.0); + float2 fragmentClipSize = float2(0.0, 0.0); + float2 fragmentPos = float2(0.0, 0.0); + float2 fragmentMaskUv = float2(0.0, 0.0); + float2 ndc = float2((pos.x / viewportSize.x) * 2.0 - 1.0, 1.0 - (pos.y / viewportSize.y) * 2.0); + gl_Position = float4(ndc.x, ndc.y, 0.0, 1.0); + fragmentUv = uv / atlasSize; + fragmentColor = float4(color); + fragmentClipPos = clipPos; + fragmentClipSize = clipSize; + fragmentPos = pos; + fragmentMaskUv = maskUv / atlasSize; + output.pos = gl_Position; + output.fragmentUv = fragmentUv; + output.fragmentColor = fragmentColor; + output.fragmentClipPos = fragmentClipPos; + output.fragmentClipSize = fragmentClipSize; + output.fragmentPos = fragmentPos; + output.fragmentMaskUv = fragmentMaskUv; + return output; +} diff --git a/src/silky/drawers/shaders/silky.vert.spv b/src/silky/drawers/shaders/silky.vert.spv index 91021cefde228490dab81fb38d3bb4e74ad2c6d2..371c782dae882b94294363f995edacb87ba1c1b5 100644 GIT binary patch literal 2536 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 literal 0 HcmV?d00001 diff --git a/src/silky/drawers/vk14.nim b/src/silky/drawers/vk14.nim index ef26710..28b136b 100644 --- a/src/silky/drawers/vk14.nim +++ b/src/silky/drawers/vk14.nim @@ -1,12 +1,26 @@ when not defined(windows): {.error: "The Silky Vulkan backend currently requires Windows.".} -import - pixie, vmath, windy -import pkg/vk14 except Window - -const - InitialVertexCapacity = 4096 +import + pixie, vmath, windy +import pkg/vk14 except Window + +when not defined(shadyBinaryShaders): + import std/os, shady, silky/drawers/shader + +const + InitialVertexCapacity = 4096 + +when not defined(shadyBinaryShaders): + const + ShaderDir = currentSourcePath().parentDir / "shaders" + VertexGlslPath = ShaderDir / "silky.vert" + FragmentGlslPath = ShaderDir / "silky.frag" + VertexSpvPath = ShaderDir / "silky.vert.spv" + FragmentSpvPath = ShaderDir / "silky.frag.spv" + +const + ShaderUniformFloatCount = 4 type DrawerVertex* {.packed.} = object @@ -50,24 +64,7 @@ type proc clampViewport(size: IVec2): IVec2 = ivec2(max(1'i32, size.x), max(1'i32, size.y)) -proc normalizeVertices( - vertices: var seq[DrawerVertex], - viewportSize: IVec2, - atlasSize: Vec2 -) = - let - width = max(1.0'f, viewportSize.x.float32) - height = max(1.0'f, viewportSize.y.float32) - for i in 0 ..< vertices.len: - let p = vertices[i].pos - vertices[i].pos = vec2( - (p.x / width) * 2.0'f - 1.0'f, - (p.y / height) * 2.0'f - 1.0'f - ) - vertices[i].uv = vertices[i].uv / atlasSize - vertices[i].maskUv = vertices[i].maskUv / atlasSize - -proc requiresSwapChainRecreate(vkResult: VkResult): bool = +proc requiresSwapChainRecreate(vkResult: VkResult): bool = let code = vkResult.int32 code == VK_SUBOPTIMAL_KHR.int32 or code == VK_ERROR_OUT_OF_DATE_KHR.int32 @@ -445,10 +442,25 @@ proc createRenderPass(ctx: VulkanContext, renderer: var VkRenderer) = ctx.device, renderPassInfo.addr, nil, renderer.renderPass.addr), "Creating render pass") -proc createGraphicsPipeline(ctx: VulkanContext, renderer: var VkRenderer) = - const - vertShaderCode = staticRead("shaders/silky.vert.spv") - fragShaderCode = staticRead("shaders/silky.frag.spv") +proc createGraphicsPipeline(ctx: VulkanContext, renderer: var VkRenderer) = + when defined(shadyBinaryShaders): + const + vertShaderCode = staticRead("shaders/silky.vert.spv") + fragShaderCode = staticRead("shaders/silky.frag.spv") + else: + const + vertShaderCode = compileSpirvShader( + toShader(silkyVert, vulkanGlsl450, shaderVertex), + VertexGlslPath, + VertexSpvPath, + binaryVertex + ) + fragShaderCode = compileSpirvShader( + toShader(silkyFrag, vulkanGlsl450, shaderFragment), + FragmentGlslPath, + FragmentSpvPath, + binaryFragment + ) let vertModule = createShaderModule(ctx.device, vertShaderCode) fragModule = createShaderModule(ctx.device, fragShaderCode) @@ -539,10 +551,10 @@ proc createGraphicsPipeline(ctx: VulkanContext, renderer: var VkRenderer) = pAttachments: colorBlendAttachment.addr, blendConstants: [0.0'f32, 0.0'f32, 0.0'f32, 0.0'f32]) - pushConstantRange = VkPushConstantRange( - stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), - offset: 0, - size: uint32(sizeof(Vec2))) + pushConstantRange = VkPushConstantRange( + stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), + offset: 0, + size: uint32(ShaderUniformFloatCount * sizeof(float32))) pipelineLayoutInfo = VkPipelineLayoutCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, setLayoutCount: 1, @@ -737,11 +749,10 @@ proc endFrame*( var vertices = newSeqOfCap[DrawerVertex](vertexCount) let quadsArr = cast[ptr UncheckedArray[DrawerVertex]](quads) - for i in 0 ..< quadCount: - vertices.add(quadsArr[i]) - vertices.normalizeVertices(drawer.viewportSize, atlasSize) - - if vertexCount > 0: + for i in 0 ..< quadCount: + vertices.add(quadsArr[i]) + + if vertexCount > 0: copyMem(drawer.renderer.vertexBufferPtr, unsafeAddr vertices[0], vertexCount * sizeof(DrawerVertex)) @@ -783,9 +794,13 @@ proc endFrame*( var vertexBuffers = [drawer.renderer.vertexBuffer] var offsets = [VkDeviceSize(0)] - var descriptorSet = drawer.renderer.descriptorSet - var viewportSizePush = vec2( - safeSize.x.float32, safeSize.y.float32) + var descriptorSet = drawer.renderer.descriptorSet + var shaderUniforms = [ + safeSize.x.float32, + safeSize.y.float32, + atlasSize.x, + atlasSize.y + ] vkCmdBeginRenderPass( commandBuffer, renderPassInfo.addr, VK_SUBPASS_CONTENTS_INLINE) @@ -796,10 +811,11 @@ proc endFrame*( commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, drawer.renderer.pipelineLayout, 0, 1, descriptorSet.addr, 0, nil) - vkCmdPushConstants( - commandBuffer, drawer.renderer.pipelineLayout, - VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), - 0, uint32(sizeof(Vec2)), viewportSizePush.addr) + vkCmdPushConstants( + commandBuffer, drawer.renderer.pipelineLayout, + VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), + 0, uint32(ShaderUniformFloatCount * sizeof(float32)), + shaderUniforms.addr) vkCmdBindVertexBuffers( commandBuffer, 0, 1, vertexBuffers[0].addr, offsets[0].addr) if vertexCount > 0: diff --git a/tests/test_shader_codegen.nim b/tests/test_shader_codegen.nim index f001069..6da3bc9 100644 --- a/tests/test_shader_codegen.nim +++ b/tests/test_shader_codegen.nim @@ -16,6 +16,23 @@ block: doAssert "(texture(atlasSampler, fragmentMaskUv)).r" in fragment doAssert "mix(vec3(1.0, 1.0, 1.0), fragmentColor.rgb, maskR)" in fragment +block: + let vertex = toShader(silkyVert, vulkanGlsl450, shaderVertex) + doAssert "#version 450" in vertex + doAssert "layout(push_constant) uniform ShadyPushConstants" in vertex + doAssert "vec2 viewportSize;" in vertex + doAssert "vec2 atlasSize;" in vertex + doAssert "layout(location = 0) in vec2 pos;" in vertex + doAssert "layout(location = 5) in vec2 maskUv;" in vertex + doAssert "layout(location = 5) out vec2 fragmentMaskUv;" in vertex + doAssert "gl_Position.y = -gl_Position.y;" in vertex + + let fragment = toShader(silkyFrag, vulkanGlsl450, shaderFragment) + doAssert "layout(set = 0, binding = 0) uniform sampler2D atlasSampler;" in fragment + doAssert "layout(location = 5) in vec2 fragmentMaskUv;" in fragment + doAssert "layout(location = 0) out vec4 fragColor;" in fragment + doAssert "(texture(atlasSampler, fragmentMaskUv)).r" in fragment + block: let vertex = toHLSL(silkyVert, shaderVertex) doAssert "cbuffer ShadyUniforms : register(b0)" in vertex From 0ca9711edccddef5f0965465e023ce86bcd8c6e2 Mon Sep 17 00:00:00 2001 From: treeform Date: Wed, 27 May 2026 08:32:23 -0700 Subject: [PATCH 3/4] Pin Shady backend dependency --- nimby.lock | 2 +- silky.nimble | 2 +- src/silky/drawers/shaders/silky.frag.hlsl | 26 ++++++++++++++-------- src/silky/drawers/shaders/silky.vert.hlsl | 2 +- src/silky/drawers/shaders/silky.vs.cso | Bin 5112 -> 5112 bytes tests/test_shader_codegen.nim | 6 +++-- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/nimby.lock b/nimby.lock index a1511d2..f4c3fb7 100644 --- a/nimby.lock +++ b/nimby.lock @@ -1,4 +1,4 @@ -shady 0.1.4 https://github.com/treeform/shady 4638b43517ac3afa474514746f2640551ad0694c +shady 0.1.5 https://github.com/treeform/shady f9ab26d946c32cd9da7c04394a1353a375a9dba5 vmath 2.0.1 https://github.com/treeform/vmath ddfcd738578c47ed3330b917f5efecf5a15128b2 pixie 5.1.0 https://github.com/treeform/pixie 5eda4949a3c8bea318cfac8e42060a8f90f3f35d chroma 1.0.0 https://github.com/treeform/chroma 2381748f92e5ea16cb2403ff7e20c6dd5443a59d diff --git a/silky.nimble b/silky.nimble index ec3f93c..f0eb61a 100644 --- a/silky.nimble +++ b/silky.nimble @@ -7,7 +7,7 @@ srcDir = "src" requires "nim >= 2.2.4" requires "pixie" -requires "shady >= 0.1.5" +requires "shady#f9ab26d946c32cd9da7c04394a1353a375a9dba5" requires "vmath" requires "windy" requires "dx12" diff --git a/src/silky/drawers/shaders/silky.frag.hlsl b/src/silky/drawers/shaders/silky.frag.hlsl index c3ade02..14789c8 100644 --- a/src/silky/drawers/shaders/silky.frag.hlsl +++ b/src/silky/drawers/shaders/silky.frag.hlsl @@ -5,15 +5,23 @@ Texture2D atlasSampler : register(t0); SamplerState atlasSamplerSampler : register(s0); -float4 PSMain( - float4 gl_FragCoord : SV_POSITION, - float2 fragmentUv : TEXCOORD0, - float4 fragmentColor : COLOR0, - float2 fragmentClipPos : TEXCOORD1, - float2 fragmentClipSize : TEXCOORD2, - float2 fragmentPos : TEXCOORD3, - float2 fragmentMaskUv : TEXCOORD4 -) : SV_TARGET { +struct PSInput { + float4 pos : SV_POSITION; + float2 fragmentUv : TEXCOORD0; + float4 fragmentColor : COLOR0; + float2 fragmentClipPos : TEXCOORD1; + float2 fragmentClipSize : TEXCOORD2; + float2 fragmentPos : TEXCOORD3; + float2 fragmentMaskUv : TEXCOORD4; +}; + +float4 PSMain(PSInput input) : SV_TARGET { + float2 fragmentUv = input.fragmentUv; + float4 fragmentColor = input.fragmentColor; + float2 fragmentClipPos = input.fragmentClipPos; + float2 fragmentClipSize = input.fragmentClipSize; + float2 fragmentPos = input.fragmentPos; + float2 fragmentMaskUv = input.fragmentMaskUv; float4 fragColor = float4(0.0, 0.0, 0.0, 0.0); if ((((fragmentPos.x < fragmentClipPos.x) || (fragmentPos.y < fragmentClipPos.y)) || (fragmentClipPos.x + fragmentClipSize.x < fragmentPos.x)) || (fragmentClipPos.y + fragmentClipSize.y < fragmentPos.y)) { discard; diff --git a/src/silky/drawers/shaders/silky.vert.hlsl b/src/silky/drawers/shaders/silky.vert.hlsl index b9ebe05..71e4317 100644 --- a/src/silky/drawers/shaders/silky.vert.hlsl +++ b/src/silky/drawers/shaders/silky.vert.hlsl @@ -1,7 +1,7 @@ // target hlsl dx12 // from silkyVert -cbuffer ShadyUniforms : register(b0) { +cbuffer ShadyUniforms0 : register(b0) { float2 viewportSize; float2 atlasSize; }; diff --git a/src/silky/drawers/shaders/silky.vs.cso b/src/silky/drawers/shaders/silky.vs.cso index 64648f7a621a0cbd615e164b9502130bdbbaafeb..25c807a78910050feba7cdc8ba14139102a61497 100644 GIT binary patch delta 133 zcmeyN{zF~FCBn&BQm;0IqcJw6gJ0vLUCWI18%5e#h0Z1NCmguLra7l$Vus2YPZL9q z$q#uZOtxkFE8w2arQ4&k{*><7H@@p~`O-Ffvd?4M%p)*?adHK>fIw)+qpxdgf*2ET Y-l&pP>zS~52DdpQ7V|bgPC@vR-tu?{0Rr{uxZZem^i^e{o)Y? zhRF|kCQPqbnVxPyfnOk5Y atlasSampler : register(t0);" in fragment doAssert "SamplerState atlasSamplerSampler : register(s0);" in fragment - doAssert "float4 gl_FragCoord : SV_POSITION" in fragment + doAssert "struct PSInput" in fragment + doAssert "float4 pos : SV_POSITION;" in fragment + doAssert "float4 PSMain(PSInput input) : SV_TARGET" in fragment doAssert "atlasSampler.Sample(atlasSamplerSampler, fragmentUv)" in fragment doAssert "(atlasSampler.Sample(atlasSamplerSampler, fragmentMaskUv)).r" in fragment doAssert "lerp(float3(1.0, 1.0, 1.0), fragmentColor.rgb, maskR)" in fragment From 00934ca6fd4f6b752fc804987a87f81be0db0653 Mon Sep 17 00:00:00 2001 From: treeform Date: Sat, 30 May 2026 06:43:42 -0700 Subject: [PATCH 4/4] f --- silky.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silky.nimble b/silky.nimble index f0eb61a..dfb1fb0 100644 --- a/silky.nimble +++ b/silky.nimble @@ -7,7 +7,7 @@ srcDir = "src" requires "nim >= 2.2.4" requires "pixie" -requires "shady#f9ab26d946c32cd9da7c04394a1353a375a9dba5" +requires "shady" requires "vmath" requires "windy" requires "dx12"