shaders.asm

src/1.getting_started/3.1.shaders/shaders.asm · 140 lines

;---------------------------------------------------------------------------------------------------
; 3.1 Shaders
;---------------------------------------------------------------------------------------------------
; Two ways to get data into a shader:
;
;   * per vertex: each vertex carries a colour, and the rasterizer blends the three across the
;     triangle on its way to the pixel shader (right)
;   * per draw: a constant buffer, D3D's uniform. The CPU fills a struct and copies it in whole
;     with Map; every vertex and pixel of the draw reads the same values (left: a tint that pulses
;     over time)
;
; From here on shaders are loaded by includes/learn/shader.asm, which does what 2.1 wrote out:
; compile vs_main and ps_main from one .hlsl file and create the input layout against the vertex
; shader. LearnOpenGL's 3.3 shaders_class introduces its Shader class the same way. The window,
; device and swap chain, written out in 1.1 and 2.1, come from includes/learn/window.asm, which is
; to these lessons what GLFW is to LearnOpenGL's.
include rt.inc
include d3d11.inc
include macros.asm
include math.asm
include learn\window.asm
include learn\shader.asm
include learn\gpu.asm

;---------------------------------------------------------------------------------------------------
.data
;---------------------------------------------------------------------------------------------------
; x, y, z, then r, g, b
align 4
left_triangle real4 -0.9, -0.5, 0.0,   1.0, 1.0, 1.0
              real4 -0.1, -0.5, 0.0,   1.0, 1.0, 1.0
              real4 -0.5,  0.5, 0.0,   1.0, 1.0, 1.0
left_triangle_size equ $ - left_triangle

right_triangle real4 0.1, -0.5, 0.0,   1.0, 0.0, 0.0
               real4 0.9, -0.5, 0.0,   0.0, 1.0, 0.0
               real4 0.5,  0.5, 0.0,   0.0, 0.0, 1.0
right_triangle_size equ $ - right_triangle

semantic_position byte "POSITION", 0
semantic_color byte "COLOR", 0
align 8
vertex_layout D3D11_INPUT_ELEMENT_DESC <offset semantic_position, 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0>
              D3D11_INPUT_ELEMENT_DESC <offset semantic_color, 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0>

; Mirrors `cbuffer frame` in the shader, register for register.
frame_constants struct
    tint vec4 <>
frame_constants ends

align 16
s shader <>
c frame_constants <>
align 8
left_vb qword 0
right_vb qword 0
constants qword 0
stride dword 24
offset_ dword 0

; timing
current_frame real4 0.0

;---------------------------------------------------------------------------------------------------
.code
;---------------------------------------------------------------------------------------------------
align 16
main proc
    invoke window_create, "LearnD3D11"

    ; build and compile our shader program
    ; ------------------------------------
    invoke shader_create, ADDR s, "src/1.getting_started/3.1.shaders/shaders.hlsl", ADDR vertex_layout, 2

    ; set up vertex data
    ; ------------------
    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR left_triangle, left_triangle_size
    mov left_vb, rax
    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR right_triangle, right_triangle_size
    mov right_vb, rax

    ; A dynamic constant buffer: the CPU may rewrite it. Its size must be a multiple of 16 bytes,
    ; the size of one HLSL register.
    invoke create_constant_buffer, sizeof frame_constants
    mov constants, rax

    ; render loop
    ; -----------
    render_loop:
        call window_poll
        test eax, eax
        jz render_loop_done

        ; per-frame time logic
        ; --------------------
        call get_time
        movss current_frame, xmm0

        ; render
        ; ------
        vcall context, ID3D11DeviceContext_OMSetRenderTargets, 1, ADDR backbuffer_rtv, 0
        vcall context, ID3D11DeviceContext_RSSetViewports, 1, ADDR viewport
        vcall context, ID3D11DeviceContext_ClearRenderTargetView, backbuffer_rtv, floats(0.2, 0.3, 0.3, 1.0)

        invoke shader_use, ADDR s
        vcall context, ID3D11DeviceContext_IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
        vcall context, ID3D11DeviceContext_PSSetConstantBuffers, 0, 1, ADDR constants

        ; WRITE_DISCARD hands back fresh memory each time, so the GPU never waits on the CPU, and
        ; the previous draw keeps the values it saw.
        ; tint = (0, sin(t) / 2 + 0.5, 0, 1)
        movss xmm0, current_frame
        call sinf
        mulss xmm0, math_half
        addss xmm0, math_half
        storef c.tint.x, 0.0
        movss c.tint.y, xmm0
        storef c.tint.z, 0.0
        storef c.tint.w, 1.0
        invoke update_buffer, constants, ADDR c, sizeof frame_constants
        vcall context, ID3D11DeviceContext_IASetVertexBuffers, 0, 1, ADDR left_vb, ADDR stride, ADDR offset_
        vcall context, ID3D11DeviceContext_Draw, 3, 0

        storef c.tint.x, 1.0
        storef c.tint.y, 1.0
        storef c.tint.z, 1.0
        invoke update_buffer, constants, ADDR c, sizeof frame_constants
        vcall context, ID3D11DeviceContext_IASetVertexBuffers, 0, 1, ADDR right_vb, ADDR stride, ADDR offset_
        vcall context, ID3D11DeviceContext_Draw, 3, 0

        ; dxgi: show the image just drawn, waiting for the next vertical blank
        ; --------------------------------------------------------------------
        call window_present
        jmp render_loop
    render_loop_done:

    invoke ExitProcess, 0
main endp

end
Part of masm64-dx11, LearnOpenGL's lessons rewritten for Direct3D 11 in x64 assembly. Licensed CC BY-NC 4.0, after LearnOpenGL by Joey de Vries.