Skip to content

Instantly share code, notes, and snippets.

@pJotoro
Last active December 1, 2023 20:17
Show Gist options
  • Save pJotoro/72d88ce6fcc4cfd016e67704455f83de to your computer and use it in GitHub Desktop.
Save pJotoro/72d88ce6fcc4cfd016e67704455f83de to your computer and use it in GitHub Desktop.
package main
import "jo:app"
import win32 "core:sys/windows"
import "vendor:directx/d3d11"
import "vendor:directx/d3d_compiler"
import "vendor:directx/dxgi"
D3D11_Context :: struct {
device: ^d3d11.IDevice1,
device_context: ^d3d11.IDeviceContext1,
dxgi_device: ^dxgi.IDevice1,
dxgi_adapter: ^dxgi.IAdapter,
dxgi_factory: ^dxgi.IFactory2,
swap_chain: ^dxgi.ISwapChain1,
framebuffer_texture: ^d3d11.ITexture2D,
framebuffer_render_target_view: ^d3d11.IRenderTargetView,
}
d3d11_init :: proc(using ctx: ^D3D11_Context) -> (hr: win32.HRESULT) {
feature_levels := [?]d3d11.FEATURE_LEVEL{._11_0}
{
base_device: ^d3d11.IDevice
base_device_context: ^d3d11.IDeviceContext
d3d11_flags := d3d11.CREATE_DEVICE_FLAGS{.SINGLETHREADED, .BGRA_SUPPORT}
when ODIN_DEBUG {
d3d11_flags += {.DEBUG}
}
hr = d3d11.CreateDevice(nil, .HARDWARE, nil, d3d11_flags, raw_data(&feature_levels), u32(len(feature_levels)), d3d11.SDK_VERSION, &base_device, nil, &base_device_context)
if hr != 0 do return
hr = base_device->QueryInterface(d3d11.IDevice1_UUID, (^rawptr)(&device))
if hr != 0 do return
hr = base_device_context->QueryInterface(d3d11.IDeviceContext1_UUID, (^rawptr)(&device_context))
if hr != 0 do return
}
hr = device->QueryInterface(dxgi.IDevice1_UUID, (^rawptr)(&dxgi_device))
if hr != 0 do return
hr = dxgi_device->GetAdapter(&dxgi_adapter)
if hr != 0 do return
hr = dxgi_adapter->GetParent(dxgi.IFactory2_UUID, (^rawptr)(&dxgi_factory))
if hr != 0 do return
{
desc := dxgi.SWAP_CHAIN_DESC1{
Format = .B8G8R8A8_UNORM,
SampleDesc = {Count = 1},
BufferUsage = {.RENDER_TARGET_OUTPUT},
BufferCount = 2,
SwapEffect = .FLIP_DISCARD,
}
hr = dxgi_factory->CreateSwapChainForHwnd(device, win32.HWND(app.window()), &desc, nil, nil, &swap_chain)
if hr != 0 do return
swap_chain->GetDesc1(&desc)
assert(int(desc.Width) == app.width())
assert(int(desc.Height) == app.height())
}
hr = swap_chain->GetBuffer(0, d3d11.ITexture2D_UUID, (^rawptr)(&framebuffer_texture))
if hr != 0 do return
hr = device->CreateRenderTargetView(framebuffer_texture, nil, &framebuffer_render_target_view)
if hr != 0 do return
return
}
d3d11_create_buffer :: proc(using ctx: ^D3D11_Context, byte_width: u32, usage: d3d11.USAGE, bind_flags: d3d11.BIND_FLAGS, cpu_access_flags: d3d11.CPU_ACCESS_FLAGS, misc_flags: d3d11.RESOURCE_MISC_FLAGS, structure_byte_stride: u32, memory: rawptr, memory_pitch, memory_slice_pitch: u32) -> (buffer: ^d3d11.IBuffer, hr: win32.HRESULT) {
desc := d3d11.BUFFER_DESC{
ByteWidth = byte_width,
Usage = usage,
BindFlags = bind_flags,
CPUAccessFlags = cpu_access_flags,
MiscFlags = misc_flags,
StructureByteStride = structure_byte_stride,
}
subresource_data := d3d11.SUBRESOURCE_DATA{
pSysMem = memory,
SysMemPitch = memory_pitch,
SysMemSlicePitch = memory_slice_pitch,
}
hr = device->CreateBuffer(&desc, memory != nil ? &subresource_data : nil, &buffer)
return
}
d3d11_compile :: proc(file_data: []byte, src_name, entry, target: cstring, loc := #caller_location) -> (blob: ^d3d_compiler.ID3D10Blob, hr: win32.HRESULT) {
hr = d3d_compiler.Compile(raw_data(file_data), len(file_data), src_name, nil, nil, entry, target, 0, 0, &blob, nil)
return
}
package directx_d3d11
import win32 "core:sys/windows"
LPCWSTR :: win32.LPCWSTR
DWORD :: win32.DWORD
IDevice1_UUID_STRING :: "a04bfb29-08ef-43d6-a49c-a9bdbdcbe686"
IDevice1_UUID := &IID{0xa04bfb29, 0x08ef, 0x43d6, {0xa4, 0x9c, 0xa9, 0xbd, 0xbd, 0xcb, 0xe6, 0x86}}
IDevice1 :: struct #raw_union {
#subtype idevice: IDevice,
using id3d11device1_vtable: ^IDevice1_VTable,
}
IDevice1_VTable :: struct {
using idevice_vtable: IDevice_VTable,
CreateBlendState1: proc "stdcall" (this: ^IDevice1, pBlendStateDesc: ^BLEND_DESC1, ppBlendState: ^^IBlendState1) -> HRESULT,
CreateDeferredContext1: proc "stdcall" (this: ^IDevice1, ContextFlags: UINT, ppDeferredContext: ^^IDeviceContext1) -> HRESULT,
CreateDeviceContextState: proc "stdcall" (this: ^IDevice1, Flags: UINT, pFeatureLevels: [^]FEATURE_LEVEL, FeatureLevels: UINT, SDKVersion: UINT, EmulatedInterface: ^IID, pChosenFeatureLevel: ^FEATURE_LEVEL, ppContextState: ^^IDeviceContextState) -> HRESULT,
CreateRasterizerState1: proc "stdcall" (this: ^IDevice1, pRasterizerDesc: ^RASTERIZER_DESC1, ppRasterizerState: ^^IRasterizerState1) -> HRESULT,
GetImmediateContext1: proc "stdcall" (this: ^IDevice1, ppImmediateContext: ^^IDeviceContext1),\
OpenSharedResource1: proc "stdcall" (this: ^IDevice1, hResource: HANDLE, returnedInterface: ^IID, ppResource: ^rawptr) -> HRESULT,
OpenSharedResourceByName: proc "stdcall" (this: ^IDevice1, lpName: LPCWSTR, dwDesiredAccess: DWORD, returnedInterface: ^IID, ppResource: ^rawptr) -> HRESULT,
}
LOGIC_OP :: enum i32 {
CLEAR = 0,
SET,
COPY,
COPY_INVERTED,
NOOP,
INVERT,
AND,
NAND,
OR,
NOR,
XOR,
EQUIV,
AND_REVERSE,
AND_INVERTED,
OR_REVERSE,
OR_INVERTED,
}
RENDER_TARGET_BLEND_DESC1 :: struct {
BlendEnable: BOOL,
LogicOpEnable: BOOL,
SrcBlend: BLEND,
DestBlend: BLEND,
BlendOp: BLEND_OP,
SrcBlendAlpha: BLEND,
DestBlendAlpha: BLEND,
BlendOpAlpha: BLEND_OP,
LogicOp: LOGIC_OP,
RenderTargetWriteMask: u8,
}
BLEND_DESC1 :: struct {
AlphaToCoverageEnable: BOOL,
IndependentBlendEnable: BOOL,
RenderTarget: [8]RENDER_TARGET_BLEND_DESC1,
}
IBlendState1_UUID_STRING :: "CC86FABE-DA55-401D-85E7-E3C9dE2877E9"
IBlendState1_UUID := &IID{0xCC86FABE, 0xDA55, 0x401D, {0x85, 0xE7, 0xE3, 0xC9, 0xdE, 0x28, 0x77, 0xE9}}
IBlendState1 :: struct #raw_union {
#subtype id3d11blendstate: IBlendState,
using id3d11blendstate1_vtable: ^IBlendState1_VTable,
}
IBlendState1_VTable :: struct {
using id3d11blendstate_vtable: IBlendState_VTable,
GetDesc1: proc "stdcall" (this: ^IBlendState1, pDesc: ^BLEND_DESC1),
}
IDeviceContext1_UUID_STRING :: "BB2C6fAA-B5FB-4082-8E6B-388B8CFA90E1"
IDeviceContext1_UUID := &IID{0xBB2C6FAA, 0xB5FB, 0x4082, {0x8E, 0x6B, 0x38, 0x8B, 0x8C, 0xFA, 0x90, 0xE1}}
IDeviceContext1 :: struct #raw_union {
#subtype id3d11devicecontext: IDeviceContext,
using id3d11devicecontext1_vtable: ^IDeviceContext1_VTable,
}
IDeviceContext1_VTable :: struct {
using id3d11devicecontext_vtable: IDeviceContext_VTable,
ClearView: proc "stdcall" (this: ^IDeviceContext1, pView: ^IView, Color: [^]f32, pRect: ^RECT, NumRects: UINT),
CopySubresourceRegion1: proc "stdcall" (this: ^IDeviceContext1, pDstResource: ^IResource, DstSubresource: UINT, DstX: UINT, DstY: UINT, DstZ: UINT, pSrcResource: ^IResource, SrcSubresource: UINT, pSrcBox: ^BOX, CopyFlags: UINT),
CSGetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
CSSetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
DiscardResource: proc "stdcall" (this: ^IDeviceContext1, pResource: ^IResource),
DiscardView: proc "stdcall" (this: ^IDeviceContext1, pResourceView: ^IView),
DiscardView1: proc "stdcall" (this: ^IDeviceContext1, pResourceView: ^IView, pRects: [^]RECT, NumRects: UINT),
DSGetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
DSSetConstantBuffesr1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
GSGetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
GSSetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
HSGetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
HSSetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
PSGetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
PSSetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
SwapDeviceContextState: proc "stdcall" (this: ^IDeviceContext1, pState: ^IDeviceContextState, ppPreviousState: ^^IDeviceContextState),
UpdateSubresource1: proc "stdcall" (this: ^IDeviceContext1, pDstResource: ^IResource, DstSubresource: UINT, pDstBox: ^BOX, pSrcData: rawptr, SrcRowPitch: UINT, SrcDepthPitch: UINT, CopyFlags: UINT),
VSGetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
VSSetConstantBuffers1: proc "stdcall" (this: ^IDeviceContext1, StartSlot: UINT, NumBuffers: UINT, ppConstantBuffers: [^]^IBuffer, pFirstConstant: ^UINT, pNumConstants: ^UINT),
}
IDeviceContextState_UUID_STRING :: "5C1E0D8A-7C23-48F9-8C59-A92958CEFF11"
IDeviceContextState_UUID := &IID{0x5C1E0D8A, 0x7C23, 0x48F9, {0x8C, 0x59, 0xA9, 0x29, 0x58, 0xCE, 0xFF, 0x11}}
IDeviceContextState :: struct #raw_union {
#subtype id3d11devicechild: IDeviceChild,
using id3d11devicecontextstate_vtable: ^IDeviceContextState_VTable,
}
IDeviceContextState_VTable :: struct {
using id3d11devicechild_vtable: IDeviceChild_VTable,
}
IRasterizerState1_UUID_STRING :: "1217D7A6-5039-418C-B042-9CBE256AFD6E"
IRasterizerState1_UUID := &IID{0x1217D7A6, 0x5039, 0x418C, {0xB0, 0x42, 0x9C, 0xBE, 0x25, 0x6A, 0xFD, 0x6E}}
IRasterizerState1 :: struct #raw_union {
#subtype id3d11rasterizerstate: IRasterizerState,
using id3d11rasterizerstate1_vtable: ^IRasterizerState1_VTable,
}
IRasterizerState1_VTable :: struct {
using id3d11rasterizerstate_vtable: IRasterizerState_VTable,
GetDesc1: proc "stdcall" (this: ^IRasterizerState1, pDesc: ^RASTERIZER_DESC1),
}
RASTERIZER_DESC1 :: struct {
FillMode: FILL_MODE,
CullMode: CULL_MODE,
FrontCounterClockwise: BOOL,
DepthBias: i32,
DepthBiasClamp: f32,
SlopeScaledDepthBias: f32,
DepthClipEnable: BOOL,
ScissorEnable: BOOL,
MultisampleEnable: BOOL,
AntialiasedLineEnable: BOOL,
ForcedSampleCount: UINT,
}
package main
import "jo:app"
import "core:image/png"
import "core:os"
import "vendor:directx/d3d11"
import "vendor:directx/d3d_compiler"
main :: proc() {
app.init()
img, err := png.load("assets/jonar_idle.png")
assert(err == nil)
assert(img.channels == 4)
using ctx: D3D11_Context
hr := d3d11_init(&ctx)
assert(hr == 0)
constant_buffer: ^d3d11.IBuffer
constant_data := [4]f32{2 / f32(app.width()), -2 / f32(app.height()), 1 / 128, 1 / 128}
constant_buffer, hr = d3d11_create_buffer(&ctx, size_of(constant_data) + 0xf & 0xfffffff0, .IMMUTABLE, {.CONSTANT_BUFFER}, nil, nil, 0, raw_data(&constant_data), 0, 0)
assert(hr == 0)
atlas_texture: ^d3d11.ITexture2D
{
desc := d3d11.TEXTURE2D_DESC{
Width = u32(img.width),
Height = u32(img.height),
MipLevels = 1,
ArraySize = 1,
Format = .R8G8B8A8_UNORM,
SampleDesc = {Count = 1},
Usage = .IMMUTABLE,
BindFlags = {.SHADER_RESOURCE},
}
atlas_subresource_data := d3d11.SUBRESOURCE_DATA{
pSysMem = raw_data(img.pixels.buf[:]),
SysMemPitch = u32(img.width * size_of(u32)),
}
hr = device->CreateTexture2D(&desc, &atlas_subresource_data, &atlas_texture)
assert(hr == 0)
}
atlas_shader_resource_view: ^d3d11.IShaderResourceView
{
hr = device->CreateShaderResourceView(atlas_texture, nil, &atlas_shader_resource_view)
assert(hr == 0)
}
Sprite :: struct {
screen_pos, size, atlas_pos: [2]i32,
}
MAX_SPRITES :: 4096
sprite_buffer: ^d3d11.IBuffer
sprite_buffer, hr = d3d11_create_buffer(&ctx, size_of(Sprite) * MAX_SPRITES, .DYNAMIC, {.SHADER_RESOURCE}, {.WRITE}, {.BUFFER_STRUCTURED}, size_of(Sprite), nil, 0, 0)
assert(hr == 0)
sprite_shader_resource_view: ^d3d11.IShaderResourceView
{
desc := d3d11.SHADER_RESOURCE_VIEW_DESC{
Format = .UNKNOWN,
ViewDimension = .BUFFER,
Buffer = {NumElements = MAX_SPRITES},
}
hr = device->CreateShaderResourceView(sprite_buffer, &desc, &sprite_shader_resource_view)
assert(hr == 0)
}
point_sampler: ^d3d11.ISamplerState
{
desc := d3d11.SAMPLER_DESC{
Filter = .MIN_MAG_MIP_POINT,
AddressU = .CLAMP,
AddressV = .CLAMP,
AddressW = .CLAMP,
ComparisonFunc = .NEVER,
}
hr = device->CreateSamplerState(&desc, &point_sampler)
assert(hr == 0)
}
vs_blob, ps_blob: ^d3d_compiler.ID3D10Blob
{
file_data, ok := os.read_entire_file("src/shaders.hlsl", context.temp_allocator)
assert(ok)
vs_blob, hr = d3d11_compile(file_data, "shaders.hlsl", "vs", "vs_5_0")
assert(hr == 0)
ps_blob, hr = d3d11_compile(file_data, "shaders.hlsl", "ps", "ps_5_0")
assert(hr == 0)
}
vertex_shader: ^d3d11.IVertexShader
hr = device->CreateVertexShader(vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), nil, &vertex_shader)
assert(hr == 0)
pixel_shader: ^d3d11.IPixelShader
hr = device->CreatePixelShader(ps_blob->GetBufferPointer(), ps_blob->GetBufferSize(), nil, &pixel_shader)
assert(hr == 0)
clear_color := [4]f32{0.1725, 0.1725, 0.1725, 1}
framebuffer_viewport := d3d11.VIEWPORT{0, 0, f32(app.width()), f32(app.height()), 0, 1}
sprite_batch := make([dynamic]Sprite, 0, MAX_SPRITES)
frame := 0
tick := 0
pos: [2]f32
for !app.should_close() {
if app.key_pressed(.Escape) do return
pos += app.gamepad_left_stick(0)
tick += 1
if tick == 20 {
tick = 0
frame += 1
if frame > 6 do frame = 0
}
clear(&sprite_batch)
sprite := Sprite{
screen_pos = {100, 100},
size = {128, 128},
atlas_pos = {i32(frame * 128), 0},
}
append(&sprite_batch, sprite)
sprite_buffer_mapped_subresource: d3d11.MAPPED_SUBRESOURCE
hr = device_context->Map(sprite_buffer, 0, .WRITE_DISCARD, nil, &sprite_buffer_mapped_subresource)
assert(hr == 0)
copy(([^]Sprite)(sprite_buffer_mapped_subresource.pData)[:len(sprite_batch)], sprite_batch[:])
device_context->Unmap(sprite_buffer, 0)
device_context->OMSetRenderTargets(1, &framebuffer_render_target_view, nil)
device_context->ClearRenderTargetView(framebuffer_render_target_view, &clear_color)
device_context->IASetPrimitiveTopology(.TRIANGLESTRIP)
device_context->VSSetShader(vertex_shader, nil, 0)
device_context->VSSetShaderResources(0, 1, &sprite_shader_resource_view)
device_context->VSSetConstantBuffers(0, 1, &constant_buffer)
device_context->RSSetViewports(1, &framebuffer_viewport)
device_context->PSSetShader(pixel_shader, nil, 0)
device_context->PSSetShaderResources(1, 1, &atlas_shader_resource_view)
device_context->PSSetSamplers(0, 1, &point_sampler)
device_context->DrawInstanced(4, u32(len(sprite_batch)), 0, 0)
hr = swap_chain->Present(1, 0)
assert(hr == 0)
free_all(context.temp_allocator)
}
}
#define FAT_PIXEL_SIZE 4
cbuffer constants : register(b0)
{
float2 rn_screensize;
float2 r_atlassize;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
struct sprite
{
int2 screenpos;
int2 size;
int2 atlaspos;
};
struct pixel
{
float4 xy : SV_POSITION;
float2 uv : UV;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
StructuredBuffer<sprite> spritebuffer : register(t0);
Texture2D<float4> atlastexture : register(t1);
SamplerState pointsampler : register(s0);
///////////////////////////////////////////////////////////////////////////////////////////////////
pixel vs(uint spriteid : SV_INSTANCEID, uint vertexid : SV_VERTEXID)
{
sprite spr = spritebuffer[spriteid];
float4 pos = float4(spr.screenpos, spr.screenpos + spr.size); // * FAT_PIXEL_SIZE
float4 tex = float4(spr.atlaspos, spr.atlaspos + spr.size);
uint2 i = { vertexid & 2, (vertexid << 1 & 2) ^ 3 };
pixel p;
p.xy = float4(float2(pos[i.x], pos[i.y]) * rn_screensize - float2(1, -1), 0, 1);
p.uv = float2(tex[i.x], tex[i.y]) * r_atlassize;
return p;
}
float4 ps(pixel p) : SV_TARGET
{
float4 color = atlastexture.Sample(pointsampler, p.uv);
if (color.a == 0) discard;
return color;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment