571 lines
20 KiB
C++
571 lines
20 KiB
C++
|
|
||
|
#define DIRECTX_MAX_TEXTURE_COUNT 32
|
||
|
|
||
|
struct DirectXTexture {
|
||
|
ID3D11Texture2D* pointer;
|
||
|
ID3D11ShaderResourceView* view;
|
||
|
};
|
||
|
|
||
|
struct GL_Program {
|
||
|
ID3D11VertexShader* vertex;
|
||
|
ID3D11InputLayout* layout;
|
||
|
ID3D11PixelShader* pixel;
|
||
|
b8 valid;
|
||
|
};
|
||
|
|
||
|
struct DirectX {
|
||
|
b8 initialized;
|
||
|
ID3D11Device1* device;
|
||
|
ID3D11DeviceContext1* context;
|
||
|
IDXGISwapChain1* swap_chain;
|
||
|
ID3D11SamplerState* sampler;
|
||
|
ID3D11RenderTargetView* render_target_view;
|
||
|
GL_Program gpu_program;
|
||
|
ID3D11Buffer* vertex_buffer;
|
||
|
ID3D11Buffer* constants_buffer;
|
||
|
|
||
|
// NOTE(simon): To keep the API the same since the OpenGL texture handle are store in other places
|
||
|
// than the graphics parts (e.g. in the font Face struct), we create an array of textures, and use
|
||
|
// the indices as texture handles.
|
||
|
DirectXTexture textures[ DIRECTX_MAX_TEXTURE_COUNT + 1 ];
|
||
|
// NOTE(simon): First slot in the array should not be used so we can consider an index of 0 to be invalid.
|
||
|
// OpenGL should not return 0 for texture handle, so we sort of do the same.
|
||
|
u32 texture_count; // TODO(simon): We don't reuse freed texture handle. Use a free list instead ?
|
||
|
};
|
||
|
|
||
|
global DirectX g_directx = { };
|
||
|
|
||
|
// NOTE(simon): Passing 0 for textid use the reserved texture in the array, and passing a resource
|
||
|
// view of zero unbinds the resource.
|
||
|
internal void
|
||
|
gl__bind_texture(Render_Target *t, i32 texid){
|
||
|
if (t->bound_texture != texid){
|
||
|
DirectXTexture* texture = g_directx.textures + texid;
|
||
|
g_directx.context->PSSetShaderResources( 0, 1, &texture->view );
|
||
|
t->bound_texture = texid;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
gl__bind_any_texture(Render_Target *t){
|
||
|
if (t->bound_texture == 0){
|
||
|
Assert(t->fallback_texture_id != 0);
|
||
|
DirectXTexture* texture = g_directx.textures + t->fallback_texture_id;
|
||
|
g_directx.context->PSSetShaderResources( 0, 1, &texture->view );
|
||
|
t->bound_texture = t->fallback_texture_id;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal u32
|
||
|
gl__get_texture(Vec3_i32 dim, Texture_Kind texture_kind){
|
||
|
|
||
|
u32 tex = 0;
|
||
|
|
||
|
if ( g_directx.texture_count < DIRECTX_MAX_TEXTURE_COUNT + 1 ) {
|
||
|
|
||
|
tex = g_directx.texture_count;
|
||
|
|
||
|
DirectXTexture* texture = g_directx.textures + tex;
|
||
|
Assert( texture->pointer == 0 );
|
||
|
Assert( texture->view == 0 );
|
||
|
|
||
|
D3D11_TEXTURE2D_DESC texture_desc = { 0 };
|
||
|
texture_desc.Width = dim.x;
|
||
|
texture_desc.Height = dim.y;
|
||
|
texture_desc.MipLevels = 1;
|
||
|
texture_desc.ArraySize = dim.z;
|
||
|
texture_desc.Format = DXGI_FORMAT_A8_UNORM;
|
||
|
texture_desc.SampleDesc.Count = 1;
|
||
|
texture_desc.Usage = D3D11_USAGE_DEFAULT;
|
||
|
texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||
|
texture_desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE;
|
||
|
|
||
|
HRESULT hr = g_directx.device->CreateTexture2D( &texture_desc, 0, &texture->pointer );
|
||
|
|
||
|
if ( SUCCEEDED( hr ) ) {
|
||
|
hr = g_directx.device->CreateShaderResourceView( ( ID3D11Resource* ) texture->pointer, 0, &texture->view );
|
||
|
}
|
||
|
|
||
|
if ( SUCCEEDED( hr ) ) {
|
||
|
g_directx.texture_count++;
|
||
|
} else {
|
||
|
tex = 0;
|
||
|
|
||
|
if ( texture->pointer ) {
|
||
|
texture->pointer->Release( );
|
||
|
texture->pointer = 0;
|
||
|
}
|
||
|
|
||
|
if ( texture->view ) {
|
||
|
texture->view->Release( );
|
||
|
texture->view = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(tex);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
gl__fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void *data){
|
||
|
|
||
|
// NOTE(simon): The OpenGL version always returns false.
|
||
|
b32 result = false;
|
||
|
|
||
|
// NOTE(simon): In the OpenGL version, if we don't pass zero as texture handle, the function
|
||
|
// works on the currently bound texture. In directx we need to get the texture pointer. We
|
||
|
// could retrieve that from Render_Target->bound_texture, but we don't have that as a parameter
|
||
|
// to this function and don't want to change the signature since it's used by the font
|
||
|
// rendering code and other platforms. Fortunately the only call that specified 0 for the
|
||
|
// texture handle was for the creation of the fallback texture in gl_render, and we can modify
|
||
|
// that call to pass the fallback texture handle.
|
||
|
Assert( texture != 0 );
|
||
|
|
||
|
if (dim.x > 0 && dim.y > 0 && dim.z > 0){
|
||
|
|
||
|
DirectXTexture* tex = g_directx.textures + texture;
|
||
|
|
||
|
D3D11_BOX box = { };
|
||
|
box.left = p.x;
|
||
|
box.right = p.x + dim.x;
|
||
|
box.top = p.y;
|
||
|
box.bottom = p.y + dim.y;
|
||
|
box.front = p.z;
|
||
|
box.back = p.z + dim.z;
|
||
|
// TODO(simon): Try to make the font renderer several texture in the array to verify this works.
|
||
|
g_directx.context->UpdateSubresource( tex->pointer, 0, &box, data, dim.x, dim.x * dim.y );
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal void gl__free_texture( u32 texture ) {
|
||
|
|
||
|
if ( texture ) {
|
||
|
|
||
|
DirectXTexture* tex = g_directx.textures + texture;
|
||
|
|
||
|
if ( tex->view ) {
|
||
|
tex->view->Release( );
|
||
|
tex->view = 0;
|
||
|
}
|
||
|
|
||
|
if ( tex->pointer ) {
|
||
|
tex->pointer->Release( );
|
||
|
tex->pointer = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
char *gl__vertex = R"foo(
|
||
|
|
||
|
// NOTE(simon): The layout of this is (constants are store in 16 bytes vectors (4 floats))
|
||
|
// vector1: view_m._11, view_m._12, 0, 0
|
||
|
// vector2: view_m._21, view_m._22, view_t.x, view_t.y
|
||
|
cbuffer constants : register( b0 ) {
|
||
|
row_major float2x2 view_m;
|
||
|
float2 view_t;
|
||
|
}
|
||
|
|
||
|
struct input_t {
|
||
|
float2 vertex_p : POSITION;
|
||
|
float3 vertex_t : UVW;
|
||
|
float4 vertex_c : COLOR;
|
||
|
float vertex_ht : THICKNESS;
|
||
|
};
|
||
|
|
||
|
struct output_t {
|
||
|
float4 position : SV_POSITION;
|
||
|
float4 color : COLOR;
|
||
|
float3 uvw : UVW;
|
||
|
float2 xy : XY;
|
||
|
float2 adjusted_half_dim: HALF_DIM;
|
||
|
float half_thickness : THICKNESS;
|
||
|
};
|
||
|
|
||
|
output_t main(input_t input) {
|
||
|
|
||
|
output_t output;
|
||
|
|
||
|
output.position = float4( mul( view_m, ( input.vertex_p - view_t ) ), 0.0, 1.0 );
|
||
|
// NOTE(simon): The input colors are BGRA, we need them as RGBA.
|
||
|
output.color = input.vertex_c.zyxw;
|
||
|
output.uvw = input.vertex_t;
|
||
|
output.xy = input.vertex_p;
|
||
|
output.half_thickness = input.vertex_ht;
|
||
|
|
||
|
float2 center = input.vertex_t.xy;
|
||
|
float2 half_dim = abs( input.vertex_p - center );
|
||
|
output.adjusted_half_dim = half_dim - input.vertex_t.zz + float2( 0.5, 0.5 );
|
||
|
|
||
|
return output;
|
||
|
}
|
||
|
)foo";
|
||
|
|
||
|
char *gl__fragment = R"foo(
|
||
|
|
||
|
struct input_t {
|
||
|
float4 position : SV_POSITION;
|
||
|
float4 color : COLOR;
|
||
|
float3 uvw : UVW;
|
||
|
float2 xy : XY;
|
||
|
float2 adjusted_half_dim: HALF_DIM;
|
||
|
float half_thickness : THICKNESS;
|
||
|
};
|
||
|
|
||
|
Texture2DArray alpha : register( t0 );
|
||
|
SamplerState alpha_sampler : register( s0 );
|
||
|
|
||
|
float rectangle_sd( float2 p, float2 b ) {
|
||
|
|
||
|
float2 d = abs( p ) - b;
|
||
|
return( length( max( d, float2( 0.0, 0.0 ) ) ) + min( max( d.x, d.y ), 0.0 ) );
|
||
|
}
|
||
|
|
||
|
float4 main( input_t input ) : SV_TARGET {
|
||
|
|
||
|
float has_thickness = step( 0.49, input.half_thickness );
|
||
|
float does_not_have_thickness = 1.0 - has_thickness;
|
||
|
|
||
|
float sample_value = alpha.Sample( alpha_sampler, input.uvw ).a;
|
||
|
sample_value *= does_not_have_thickness;
|
||
|
|
||
|
float2 center = input.uvw.xy;
|
||
|
float roundness = input.uvw.z;
|
||
|
float sd = rectangle_sd( input.xy - center, input.adjusted_half_dim );
|
||
|
sd = sd - roundness;
|
||
|
sd = abs( sd + input.half_thickness ) - input.half_thickness;
|
||
|
float shape_value = 1.0 - smoothstep(-1.0, 0.0, sd);
|
||
|
shape_value *= has_thickness;
|
||
|
|
||
|
float4 result = float4( input.color.xyz, input.color.a * ( sample_value + shape_value ) );
|
||
|
return result;
|
||
|
}
|
||
|
)foo";
|
||
|
|
||
|
// NOTE(simon): This function is not generic. It can compile any shader, but the vertex input
|
||
|
// layout is fixed. 4coder only has one vertex format and shader, so we could remove this function
|
||
|
// and move its content in the win32_gl_create_window. I removed the header parameter as it's not
|
||
|
// useful in directx.
|
||
|
internal GL_Program
|
||
|
gl__make_program( char* vertex, char* pixel ) {
|
||
|
|
||
|
GL_Program result = { };
|
||
|
|
||
|
u32 vertex_length = 0;
|
||
|
|
||
|
while ( vertex && vertex[ vertex_length ] != 0 ) {
|
||
|
vertex_length++;
|
||
|
}
|
||
|
|
||
|
u32 pixel_length = 0;
|
||
|
|
||
|
while ( pixel && pixel[ pixel_length ] != 0 ) {
|
||
|
pixel_length++;
|
||
|
}
|
||
|
|
||
|
ID3DBlob* vs_blob = 0;
|
||
|
ID3DBlob* vs_error_blob = 0;
|
||
|
ID3D11VertexShader* vertex_shader = 0;
|
||
|
ID3D11InputLayout* input_layout = 0;
|
||
|
|
||
|
ID3DBlob* ps_blob = 0;
|
||
|
ID3DBlob* ps_error_blob = 0;
|
||
|
ID3D11PixelShader* pixel_shader = 0;
|
||
|
|
||
|
do {
|
||
|
|
||
|
HRESULT hr = D3DCompile( vertex, vertex_length, 0, 0, 0, "main", "vs_5_0", 0, 0, &vs_blob, &vs_error_blob );
|
||
|
|
||
|
if ( FAILED( hr ) ) {
|
||
|
log_os( "Failed to compile vertex shader.\n" );
|
||
|
|
||
|
if ( vs_error_blob ) {
|
||
|
u8* error_message = ( u8* ) vs_error_blob->GetBufferPointer( );
|
||
|
u32 length = ( u32 ) vs_error_blob->GetBufferSize( );
|
||
|
log_os( "vertex shader error:\n%.*s\n", length, error_message );
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
hr = g_directx.device->CreateVertexShader( vs_blob->GetBufferPointer( ), vs_blob->GetBufferSize( ), 0, &vertex_shader );
|
||
|
|
||
|
if ( FAILED( hr ) ) {
|
||
|
log_os( "Failed to create a vertex shader.\n" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
D3D11_INPUT_ELEMENT_DESC layout_desc[ 4 ] = { };
|
||
|
|
||
|
layout_desc[ 0 ].SemanticName = "POSITION";
|
||
|
layout_desc[ 0 ].Format = DXGI_FORMAT_R32G32_FLOAT;
|
||
|
layout_desc[ 0 ].AlignedByteOffset = 0;
|
||
|
layout_desc[ 0 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||
|
|
||
|
layout_desc[ 1 ].SemanticName = "UVW";
|
||
|
layout_desc[ 1 ].Format = DXGI_FORMAT_R32G32B32_FLOAT;
|
||
|
layout_desc[ 1 ].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
||
|
layout_desc[ 1 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||
|
|
||
|
layout_desc[ 2 ].SemanticName = "COLOR";
|
||
|
layout_desc[ 2 ].Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||
|
layout_desc[ 2 ].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
||
|
layout_desc[ 2 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||
|
|
||
|
layout_desc[ 3 ].SemanticName = "THICKNESS";
|
||
|
layout_desc[ 3 ].Format = DXGI_FORMAT_R32_FLOAT;
|
||
|
layout_desc[ 3 ].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
||
|
layout_desc[ 3 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||
|
|
||
|
hr = g_directx.device->CreateInputLayout( layout_desc, ArrayCount( layout_desc ), vs_blob->GetBufferPointer( ), vs_blob->GetBufferSize( ), &input_layout );
|
||
|
|
||
|
if ( FAILED( hr ) ) {
|
||
|
log_os( "Failed to create input layout.\n" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
hr = D3DCompile( pixel, pixel_length, 0, 0, 0, "main", "ps_5_0", 0, 0, &ps_blob, &ps_error_blob );
|
||
|
|
||
|
if ( FAILED( hr ) ) {
|
||
|
log_os( "Failed to compile pixel shader.\n" );
|
||
|
|
||
|
if ( ps_error_blob ) {
|
||
|
u8* error_message = ( u8* ) ps_error_blob->GetBufferPointer( );
|
||
|
u32 length = ( u32 ) ps_error_blob->GetBufferSize( );
|
||
|
log_os( "pixel shader error:\n%.*s\n", length, error_message );
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
hr = g_directx.device->CreatePixelShader( ps_blob->GetBufferPointer( ), ps_blob->GetBufferSize( ), 0, &pixel_shader );
|
||
|
|
||
|
if ( FAILED( hr ) ) {
|
||
|
log_os( "Failed to create a pixel shader.\n" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
result.vertex = vertex_shader;
|
||
|
result.layout = input_layout;
|
||
|
result.pixel = pixel_shader;
|
||
|
result.valid = true;
|
||
|
|
||
|
} while ( 0 );
|
||
|
|
||
|
if ( vs_blob ) {
|
||
|
vs_blob->Release( );
|
||
|
vs_blob = 0;
|
||
|
}
|
||
|
|
||
|
if ( vs_error_blob ) {
|
||
|
vs_error_blob->Release( );
|
||
|
vs_error_blob = 0;
|
||
|
}
|
||
|
|
||
|
if ( ps_blob ) {
|
||
|
ps_blob->Release( );
|
||
|
ps_blob = 0;
|
||
|
}
|
||
|
|
||
|
if ( ps_error_blob ) {
|
||
|
ps_error_blob->Release( );
|
||
|
ps_error_blob = 0;
|
||
|
}
|
||
|
|
||
|
if ( !result.valid ) {
|
||
|
|
||
|
if ( vertex_shader ) {
|
||
|
vertex_shader->Release( );
|
||
|
vertex_shader = 0;
|
||
|
}
|
||
|
|
||
|
if ( input_layout ) {
|
||
|
input_layout->Release( );
|
||
|
input_layout = 0;
|
||
|
}
|
||
|
|
||
|
if ( pixel_shader ) {
|
||
|
pixel_shader->Release( );
|
||
|
pixel_shader = 0;
|
||
|
}
|
||
|
|
||
|
os_popup_error( "Error", "Shader compilation failed." );
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
gl_render(Render_Target *t){
|
||
|
Font_Set *font_set = (Font_Set*)t->font_set;
|
||
|
|
||
|
local_persist b32 first_call = true;
|
||
|
|
||
|
if (first_call){
|
||
|
|
||
|
// NOTE(simon): Most of the code here has been moved in win32_gl_create_window because if
|
||
|
// that code fails we should exit the application directly.
|
||
|
first_call = false;
|
||
|
|
||
|
u32 stride = sizeof( Render_Vertex );
|
||
|
u32 offset = 0;
|
||
|
|
||
|
g_directx.context->IASetVertexBuffers( 0, 1, &g_directx.vertex_buffer, &stride, &offset );
|
||
|
g_directx.context->IASetInputLayout( g_directx.gpu_program.layout );
|
||
|
g_directx.context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
|
||
|
|
||
|
g_directx.context->VSSetShader( g_directx.gpu_program.vertex, 0, 0 );
|
||
|
g_directx.context->VSSetConstantBuffers( 0, 1, &g_directx.constants_buffer );
|
||
|
|
||
|
g_directx.context->PSSetShader( g_directx.gpu_program.pixel, 0, 0 );
|
||
|
g_directx.context->PSSetSamplers( 0, 1, &g_directx.sampler );
|
||
|
|
||
|
{
|
||
|
t->fallback_texture_id = gl__get_texture(V3i32(2, 2, 1), TextureKind_Mono);
|
||
|
u8 white_block[] = { 0xFF, 0xFF, 0xFF, 0xFF, };
|
||
|
// NOTE(simon): Passing the fallback texture, because we can't rely on the fact that
|
||
|
// gl__get_texture has bound the fallback texture.
|
||
|
gl__fill_texture(TextureKind_Mono, t->fallback_texture_id, V3i32(0, 0, 0), V3i32(2, 2, 1), white_block);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NOTE(simon): OMSetRenderTargets needs to be set each frame when using a FLIP swap chain.
|
||
|
g_directx.context->OMSetRenderTargets( 1, &g_directx.render_target_view, 0 );
|
||
|
|
||
|
i32 width = t->width;
|
||
|
i32 height = t->height;
|
||
|
|
||
|
// NOTE(simon): Viewport (0, 0) is top left in directx. Important for viewport and scissor calls.
|
||
|
|
||
|
D3D11_VIEWPORT viewport = {
|
||
|
0, // TopLeftX
|
||
|
0, // TopLeftY
|
||
|
( float ) width, // Width
|
||
|
( float ) height, // Height
|
||
|
0, // MinDepth
|
||
|
1// MaxDepth
|
||
|
};
|
||
|
|
||
|
g_directx.context->RSSetViewports( 1, &viewport );
|
||
|
|
||
|
D3D11_RECT scissor = {
|
||
|
0, // left
|
||
|
0, // top
|
||
|
width, // right
|
||
|
height // bottom
|
||
|
};
|
||
|
|
||
|
g_directx.context->RSSetScissorRects( 1, &scissor );
|
||
|
|
||
|
float magenta[ 4 ] = { 1.0f, 0.0f, 1.0f, 1.0f };
|
||
|
g_directx.context->ClearRenderTargetView( g_directx.render_target_view, magenta );
|
||
|
|
||
|
// NOTE(simon): The constants (uniforms) were set in the render loop in the OpenGL version.
|
||
|
// But since they don't vary between draw calls I moved the code before the render loop.
|
||
|
D3D11_MAPPED_SUBRESOURCE constants_map = { };
|
||
|
HRESULT hr = g_directx.context->Map( ( ID3D11Resource* ) g_directx.constants_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &constants_map );
|
||
|
|
||
|
// NOTE(simon): The layout of the constants buffer was a bit confusing. This link explains a
|
||
|
// little about how data is laid out: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
|
||
|
// The article doesn't explain anything about matrices. What I found out while making this work is
|
||
|
// that each row or column (depending on if we use column or row major matrices) of a matrix
|
||
|
// needs to start on a new 16 bytes vector. For a 2 by 2 matrix, this means that there are two
|
||
|
// register elements at the end of the first vector that aren't used.
|
||
|
// Another thing is that the second vector only needs the first two elements for the matrix,
|
||
|
// so the two elements we want to put next can be in the same vector.
|
||
|
|
||
|
// NOTE(simon): The code here could be shorter, but I prefer to make it clear what's happening.
|
||
|
f32 view_m[ 4 ] = {
|
||
|
2.0f / width, 0,
|
||
|
0, -2.0f / height
|
||
|
};
|
||
|
f32 view_t[ 2 ] = { width / 2.0f, height / 2.0f };
|
||
|
|
||
|
f32* vector_1 = ( f32* ) constants_map.pData;
|
||
|
f32* vector_2 = vector_1 + 4;
|
||
|
|
||
|
vector_1[ 0 ] = view_m[ 0 ];
|
||
|
vector_1[ 1 ] = view_m[ 1 ];
|
||
|
vector_1[ 2 ] = 0; // Padding
|
||
|
vector_1[ 3 ] = 0; // Padding
|
||
|
|
||
|
vector_2[ 0 ] = view_m[ 2 ];
|
||
|
vector_2[ 1 ] = view_m[ 3 ];
|
||
|
vector_2[ 2 ] = view_t[ 0 ];
|
||
|
vector_2[ 3 ] = view_t[ 1 ];
|
||
|
|
||
|
g_directx.context->Unmap( ( ID3D11Resource* ) g_directx.constants_buffer, 0 );
|
||
|
|
||
|
gl__bind_texture( t, 0 );
|
||
|
|
||
|
for (Render_Free_Texture *free_texture = t->free_texture_first;
|
||
|
free_texture != 0;
|
||
|
free_texture = free_texture->next){
|
||
|
|
||
|
gl__free_texture( free_texture->tex_id );
|
||
|
}
|
||
|
|
||
|
t->free_texture_first = 0;
|
||
|
t->free_texture_last = 0;
|
||
|
|
||
|
for (Render_Group *group = t->group_first;
|
||
|
group != 0;
|
||
|
group = group->next){
|
||
|
Rect_i32 box = Ri32(group->clip_box);
|
||
|
|
||
|
D3D11_RECT group_scissor = { };
|
||
|
group_scissor.left = box.x0;
|
||
|
group_scissor.right = box.x1;
|
||
|
group_scissor.top = box.y0;
|
||
|
group_scissor.bottom = box.y1;
|
||
|
|
||
|
g_directx.context->RSSetScissorRects( 1, &group_scissor );
|
||
|
|
||
|
i32 vertex_count = group->vertex_list.vertex_count;
|
||
|
if (vertex_count > 0){
|
||
|
Face *face = font_set_face_from_id(font_set, group->face_id);
|
||
|
if (face != 0){
|
||
|
gl__bind_texture(t, face->texture);
|
||
|
}
|
||
|
else{
|
||
|
gl__bind_any_texture(t);
|
||
|
}
|
||
|
|
||
|
// NOTE(simon): We fill the buffer, draw what we filled and then do the next group,
|
||
|
// which allows to always start drawing from vertex 0. Alternatively we could do a pass
|
||
|
// to fill the vertex buffer completly so we only map the vertex buffer once, and then
|
||
|
// a second pass that just execute the draw calls. It doesn't seems necessary since we
|
||
|
// have less than 10 draw call.
|
||
|
|
||
|
D3D11_MAPPED_SUBRESOURCE vertex_map = { };
|
||
|
hr = g_directx.context->Map( ( ID3D11Resource* ) g_directx.vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &vertex_map );
|
||
|
|
||
|
if ( FAILED( hr ) ) {
|
||
|
// NOTE(simon): It's improbable that Map will fail, but if it does we just stop
|
||
|
// rendering, and we'll try on the next frame. We could just skip the group and try
|
||
|
// with the next (using 'continue' instead of 'break'), but Map would probably fail
|
||
|
// again. Waiting for the next frame "might" work. I don't really know. We could
|
||
|
// also just exit the application assuming we won't be able to render anything.
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
u8* bytes = ( u8* ) vertex_map.pData;
|
||
|
|
||
|
for (Render_Vertex_Array_Node *node = group->vertex_list.first;
|
||
|
node != 0;
|
||
|
node = node->next){
|
||
|
|
||
|
i32 size = node->vertex_count*sizeof(*node->vertices);
|
||
|
memcpy( bytes, node->vertices, size );
|
||
|
bytes += size;
|
||
|
}
|
||
|
|
||
|
g_directx.context->Unmap( ( ID3D11Resource* ) g_directx.vertex_buffer, 0 );
|
||
|
|
||
|
g_directx.context->Draw( vertex_count, 0 );
|
||
|
}
|
||
|
}
|
||
|
}
|