stencil_testing.asm

src/4.advanced_d3d11/2.stencil_testing/stencil_testing.asm · 201 lines

;---------------------------------------------------------------------------------------------------
; 2. Stencil testing
;---------------------------------------------------------------------------------------------------
; The stencil buffer is eight bits per pixel, beside the depth, that shaders never see. Draws can
; write to it and be masked by it. Here it draws an outline around each cube:
;
;   1. draw the cubes normally, and write 1 into the stencil wherever they land
;   2. draw them again a little larger, in one flat colour, only where the stencil is NOT 1, so
;      only the rim that sticks out past the originals
;
; The second pass ignores depth, so the outline shows through everything: the selected-object look
; of an editor. In D3D all of this is one immutable depth-stencil state object per configuration,
; and the reference value (1) is given when it is bound.
include rt.inc
include d3d11.inc
include macros.asm
include math.asm
include learn\window.asm
include learn\shader.asm
include learn\gpu.asm
include learn\cube.asm
include learn\camera.asm
include learn\transforms.asm

;---------------------------------------------------------------------------------------------------
.data
;---------------------------------------------------------------------------------------------------
align 4
plane_vertices real4 -5.0, -0.5,  5.0,   0.0, 1.0, 0.0,   0.0, 2.0
               real4  5.0, -0.5,  5.0,   0.0, 1.0, 0.0,   2.0, 2.0
               real4  5.0, -0.5, -5.0,   0.0, 1.0, 0.0,   2.0, 0.0
               real4 -5.0, -0.5,  5.0,   0.0, 1.0, 0.0,   0.0, 2.0
               real4  5.0, -0.5, -5.0,   0.0, 1.0, 0.0,   2.0, 0.0
               real4 -5.0, -0.5, -5.0,   0.0, 1.0, 0.0,   0.0, 0.0
plane_vertices_size equ 6 * 32

align 4
camera_start vec3 <-1.0, 1.0, 4.0>
positions vec3 <-1.0, 0.0, -1.0>
          vec3 <2.0, 0.0, 0.0>

align 16
t transforms <>
model mat4 <>
s shader <>
single_color shader <>
cam camera <>
align 8
cube_vb qword 0
plane_vb qword 0
transforms_buffer qword 0
cube_texture qword 0
floor_texture qword 0
sampler qword 0
plain_state qword 0          ; ID3D11DepthStencilState: depth as usual, stencil untouched
mark_state qword 0           ; depth as usual, and the reference value written into the stencil
outside_state qword 0        ; no depth, and only where the stencil is not the reference value

; timing
delta_time real4 0.0
last_frame real4 0.0

;---------------------------------------------------------------------------------------------------
.code
;---------------------------------------------------------------------------------------------------
; rcx: stencil comparison, rdx: 1 to write the stencil, r8: 1 to test depth -> rax: the state
align 16
create_stencil_state proc func:qword, write:qword, depth:qword
    local desc:D3D11_DEPTH_STENCIL_DESC, state:qword
    invoke mem_zero, ADDR desc, sizeof D3D11_DEPTH_STENCIL_DESC
    mov eax, dword ptr depth
    mov desc.DepthEnable, eax
    mov desc.DepthWriteMask, D3D11_DEPTH_WRITE_MASK_ALL
    mov desc.DepthFunc, D3D11_COMPARISON_LESS
    mov desc.StencilEnable, 1
    mov desc.StencilReadMask, D3D11_DEFAULT_STENCIL_READ_MASK
    mov desc.StencilWriteMask, 0
    cmp write, 0
    je no_write
        mov desc.StencilWriteMask, D3D11_DEFAULT_STENCIL_WRITE_MASK
    no_write:
    mov eax, dword ptr func
    mov desc.FrontFace.StencilFunc, eax
    mov desc.FrontFace.StencilPassOp, D3D11_STENCIL_OP_REPLACE
    mov desc.FrontFace.StencilFailOp, D3D11_STENCIL_OP_KEEP
    mov desc.FrontFace.StencilDepthFailOp, D3D11_STENCIL_OP_KEEP
    ; the back faces the same
    mov rax, qword ptr desc.FrontFace
    mov qword ptr desc.BackFace, rax
    mov rax, qword ptr desc.FrontFace[8]
    mov qword ptr desc.BackFace[8], rax
    vcall device, ID3D11Device_CreateDepthStencilState, ADDR desc, ADDR state
    AssertHr
    mov rax, state
    ret
create_stencil_state endp

align 16
main proc
    USING rbx, rsi
    SaveRegs
    invoke window_create, "LearnD3D11"
    call window_create_depth

    invoke create_sampler, D3D11_FILTER_ANISOTROPIC, D3D11_TEXTURE_ADDRESS_WRAP
    mov sampler, rax
    vcall context, ID3D11DeviceContext_PSSetSamplers, 0, 1, ADDR sampler

    invoke shader_create, ADDR s, "src/4.advanced_d3d11/2.stencil_testing/stencil_testing.hlsl", ADDR cube_layout, cube_layout_count
    invoke shader_create, ADDR single_color, "src/4.advanced_d3d11/2.stencil_testing/single_color.hlsl", ADDR cube_layout, cube_layout_count

    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR cube_vertices, cube_vertices_size
    mov cube_vb, rax
    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR plane_vertices, plane_vertices_size
    mov plane_vb, rax
    invoke create_constant_buffer, sizeof transforms
    mov transforms_buffer, rax
    invoke filesystem_path, "resources/textures/marble.jpg"
    invoke load_texture, rax, 0
    mov cube_texture, rax
    invoke filesystem_path, "resources/textures/metal.png"
    invoke load_texture, rax, 0
    mov floor_texture, rax

    lea rcx, cam
    lea rdx, camera_start
    movf xmm2, -75.0
    movf xmm3, -15.0
    call camera_create

    invoke create_stencil_state, D3D11_COMPARISON_ALWAYS, 0, 1
    mov plain_state, rax
    invoke create_stencil_state, D3D11_COMPARISON_ALWAYS, 1, 1
    mov mark_state, rax
    invoke create_stencil_state, D3D11_COMPARISON_NOT_EQUAL, 0, 0
    mov outside_state, rax

    render_loop:
        call window_poll
        test eax, eax
        jz render_loop_done

        call get_time
        movss xmm1, xmm0
        subss xmm0, last_frame
        movss delta_time, xmm0
        movss last_frame, xmm1

        invoke camera_process_input, ADDR cam, ADDR delta_time

        invoke window_begin, floats(0.1, 0.1, 0.1, 1.0)
        vcall context, ID3D11DeviceContext_IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
        invoke bind_constants, 0, transforms_buffer
        invoke transforms_from_camera, ADDR t, ADDR cam
        invoke shader_use, ADDR s

        ; the floor, leaving the stencil alone
        vcall context, ID3D11DeviceContext_OMSetDepthStencilState, plain_state, 0
        invoke bind_texture, 0, floor_texture
        invoke m4_identity, ADDR model
        invoke draw_vertices, transforms_buffer, ADDR t, plane_vb, 6, ADDR model

        ; 1. the cubes, writing 1 into the stencil
        vcall context, ID3D11DeviceContext_OMSetDepthStencilState, mark_state, 1
        invoke bind_texture, 0, cube_texture
        lea rsi, positions
        xor ebx, ebx
        cubes:
            invoke m4_translate, ADDR model, rsi
            invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR model
            add rsi, sizeof vec3
            inc ebx
            cmp ebx, 2
            jb cubes

        ; 2. a little larger, flat, only where the stencil is not 1: the rim
        vcall context, ID3D11DeviceContext_OMSetDepthStencilState, outside_state, 1
        invoke shader_use, ADDR single_color
        lea rsi, positions
        xor ebx, ebx
        outlines:
            lea rcx, model
            mov rdx, rsi
            movf xmm2, 1.1
            call m4_translate_scale
            invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR model
            add rsi, sizeof vec3
            inc ebx
            cmp ebx, 2
            jb outlines
        vcall context, ID3D11DeviceContext_OMSetDepthStencilState, 0, 0

        call window_present
        jmp render_loop
    render_loop_done:

    RestoreRegs
    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.