code/bin/4ed_build.cpp: Removed OpenGL.lib. It's added with #pragma comment in win32_opengl.cpp so that we don't need to do special things to keep OpenGL and DirectX both working.
code/platform_win32/win32_4ed.cpp: - Modification to be able to choose between OpenGL and DirectX. You choose between the two by defining WIN32_DIRECTX macro. If you don't define it it uses OpenGL. - Note that win32_gl_create_window and other part of the file that depend on OpenGL or DirectX have been moved to win32_opengl.cpp and win32_directx.cpp. - Fixed os_popup_error using the title as the message and the message as the title. code/platform_win32/win32_opengl.cpp: Contains previous code that was in win32_4ed.cpp. code/platform_win32/win32_directx.cpp: Code for creating a DirectX window and context. code/platform_win32/4ed_directx_render.cpp: Imlementation of the 4coder renderer using DirectX.
This commit is contained in:
parent
f2abe27704
commit
911df4ce05
|
@ -185,7 +185,7 @@ get_defines_from_flags(Arena *arena, u32 flags){
|
|||
"-wd4611 -wd4189 -WX -GR- -EHa- -nologo -FC"
|
||||
|
||||
#define CL_LIBS_COMMON \
|
||||
"user32.lib winmm.lib gdi32.lib opengl32.lib comdlg32.lib userenv.lib "
|
||||
"user32.lib winmm.lib gdi32.lib comdlg32.lib userenv.lib "
|
||||
#define CL_LIBS_X64 CL_LIBS_COMMON FOREIGN_WIN "\\x64\\freetype.lib"
|
||||
#define CL_LIBS_X86 CL_LIBS_COMMON FOREIGN_WIN "\\x86\\freetype.lib"
|
||||
|
||||
|
|
|
@ -0,0 +1,571 @@
|
|||
|
||||
#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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@
|
|||
// #define FPS 144
|
||||
// #define frame_useconds (1000000 / FPS)
|
||||
|
||||
#define WIN32_DIRECTX
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "4coder_base_types.h"
|
||||
|
@ -56,7 +58,6 @@
|
|||
#define function static
|
||||
|
||||
#include "win32_utf8.h"
|
||||
#include "win32_gl.h"
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
|
@ -649,18 +650,18 @@ system_cli_end_update_sig(){
|
|||
|
||||
function void
|
||||
os_popup_error(char *title, char *message){
|
||||
MessageBoxA(0, title, message, MB_OK);
|
||||
MessageBoxA(0, message, title, MB_OK);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
#include "4ed_font_provider_freetype.h"
|
||||
#include "4ed_font_provider_freetype.cpp"
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include "opengl/4ed_opengl_defines.h"
|
||||
#define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0;
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
#include "opengl/4ed_opengl_render.cpp"
|
||||
#if defined( WIN32_DIRECTX )
|
||||
#include "win32_directx.cpp"
|
||||
#else
|
||||
#include "win32_opengl.cpp"
|
||||
#endif
|
||||
|
||||
internal
|
||||
graphics_get_texture_sig(){
|
||||
|
@ -823,6 +824,72 @@ win32_resize(i32 width, i32 height){
|
|||
if (width > 0 && height > 0){
|
||||
target.width = width;
|
||||
target.height = height;
|
||||
|
||||
#if defined( WIN32_DIRECTX )
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
ID3D11Texture2D* frame_buffer = 0;
|
||||
|
||||
do {
|
||||
|
||||
if ( g_directx.initialized ) {
|
||||
|
||||
if ( g_directx.render_target_view ) {
|
||||
g_directx.render_target_view->Release( );
|
||||
g_directx.render_target_view = 0;
|
||||
}
|
||||
|
||||
hr = g_directx.swap_chain->ResizeBuffers( 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0 );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to resize the swap chain buffers.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
hr = g_directx.swap_chain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( void** ) &frame_buffer );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failled to get the swap chain back buffer.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
D3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc = { 0 };
|
||||
// NOTE(simon): 4coder checks for sRGB support but never actually enables it in the
|
||||
// OpenGL version. Note that enabling it would require to convert collors passed to the
|
||||
// shader to linear (when using sRBG back buffer, the shader values must be linear
|
||||
// values). This would be more problematic than just passing linear values as the
|
||||
// blending wouldn't produce the same result as with sRGB off.
|
||||
// render_target_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
render_target_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
render_target_view_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
||||
|
||||
hr = g_directx.device->CreateRenderTargetView( ( ID3D11Resource* ) frame_buffer, &render_target_view_desc, &g_directx.render_target_view );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a render target view.\n" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while ( 0 );
|
||||
|
||||
if ( frame_buffer ) {
|
||||
frame_buffer->Release( );
|
||||
frame_buffer = 0;
|
||||
}
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
|
||||
if ( g_directx.render_target_view ) {
|
||||
g_directx.render_target_view->Release( );
|
||||
g_directx.render_target_view = 0;
|
||||
}
|
||||
|
||||
// NOTE(simon): Failing here means no rendering possible, so we exit.
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -901,10 +968,10 @@ internal void
|
|||
date_time_from_win32_system_time(Date_Time *out, SYSTEMTIME *in){
|
||||
out->year = in->wYear;
|
||||
out->mon = (u8)(in->wMonth - 1);
|
||||
out->day = (u8)(in->wDay - 1);
|
||||
out->hour = (u8)(in->wHour);
|
||||
out->min = (u8)(in->wMinute);
|
||||
out->sec = (u8)(in->wSecond);
|
||||
out->day = (u8)(in->wDay - 1);
|
||||
out->hour = (u8)(in->wHour);
|
||||
out->min = (u8)(in->wMinute);
|
||||
out->sec = (u8)(in->wSecond);
|
||||
out->msec = in->wMilliseconds;
|
||||
}
|
||||
|
||||
|
@ -1191,7 +1258,7 @@ win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
|
|||
}
|
||||
}break;
|
||||
}
|
||||
|
||||
|
||||
b8 ctrl = (controls->r_ctrl || (controls->l_ctrl && !controls->r_alt));
|
||||
b8 alt = (controls->l_alt || (controls->r_alt && !controls->l_ctrl));
|
||||
if (win32vars.lctrl_lalt_is_altgr && controls->l_alt && controls->l_ctrl){
|
||||
|
@ -1432,249 +1499,7 @@ win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
|
|||
|
||||
//-
|
||||
|
||||
internal b32
|
||||
win32_wgl_good(Void_Func *f){
|
||||
return(f != 0 &&
|
||||
f != (Void_Func*)1 &&
|
||||
f != (Void_Func*)2 &&
|
||||
f != (Void_Func*)3 &&
|
||||
f != (Void_Func*)-1);
|
||||
}
|
||||
|
||||
typedef HGLRC (CALL_CONVENTION wglCreateContextAttribsARB_Function)(HDC,HGLRC,i32*);
|
||||
typedef BOOL (CALL_CONVENTION wglChoosePixelFormatARB_Function)(HDC,i32*,f32*,u32,i32*,u32*);
|
||||
typedef char* (CALL_CONVENTION wglGetExtensionsStringEXT_Function)();
|
||||
typedef VOID (CALL_CONVENTION wglSwapIntervalEXT_Function)(i32);
|
||||
|
||||
global wglCreateContextAttribsARB_Function *wglCreateContextAttribsARB = 0;
|
||||
global wglChoosePixelFormatARB_Function *wglChoosePixelFormatARB = 0;
|
||||
global wglGetExtensionsStringEXT_Function *wglGetExtensionsStringEXT = 0;
|
||||
global wglSwapIntervalEXT_Function *wglSwapIntervalEXT = 0;
|
||||
|
||||
internal b32
|
||||
win32_gl_create_window(HWND *wnd_out, HGLRC *context_out, DWORD style, RECT rect){
|
||||
HINSTANCE this_instance = GetModuleHandle(0);
|
||||
|
||||
local_persist b32 srgb_support = false;
|
||||
local_persist b32 register_success = true;
|
||||
local_persist b32 first_call = true;
|
||||
if (first_call){
|
||||
log_os(" GL bootstrapping...\n");
|
||||
|
||||
first_call = false;
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap window
|
||||
log_os(" registering bootstrap class...\n");
|
||||
WNDCLASSW wglclass = {};
|
||||
wglclass.lpfnWndProc = DefWindowProcW;
|
||||
wglclass.hInstance = this_instance;
|
||||
wglclass.lpszClassName = L"wgl-loader";
|
||||
if (RegisterClassW(&wglclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap window...\n");
|
||||
HWND wglwindow = CreateWindowW(wglclass.lpszClassName, L"", 0, 0, 0, 0, 0,
|
||||
0, 0, this_instance, 0);
|
||||
if (wglwindow == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap context
|
||||
log_os(" setting bootstrap pixel format...\n");
|
||||
|
||||
HDC wgldc = GetDC(wglwindow);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
format.nSize = sizeof(format);
|
||||
format.nVersion = 1;
|
||||
format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
|
||||
format.iPixelType = PFD_TYPE_RGBA;
|
||||
format.cColorBits = 32;
|
||||
format.cAlphaBits = 8;
|
||||
format.cDepthBits = 24;
|
||||
format.iLayerType = PFD_MAIN_PLANE;
|
||||
i32 suggested_format_index = ChoosePixelFormat(wgldc, &format);
|
||||
if (!SetPixelFormat(wgldc, suggested_format_index, &format)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap GL context...\n");
|
||||
HGLRC wglcontext = wglCreateContext(wgldc);
|
||||
if (wglcontext == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" making bootstrap GL context current...\n");
|
||||
if (!wglMakeCurrent(wgldc, wglcontext)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Load wgl extensions
|
||||
log_os(" loading wgl extensions...\n");
|
||||
|
||||
#define LoadWGL(f,l) Stmnt((f) = (f##_Function*)wglGetProcAddress(#f); \
|
||||
(l) = (l) && win32_wgl_good((Void_Func*)(f));)
|
||||
|
||||
b32 load_success = true;
|
||||
LoadWGL(wglCreateContextAttribsARB, load_success);
|
||||
LoadWGL(wglChoosePixelFormatARB, load_success);
|
||||
LoadWGL(wglGetExtensionsStringEXT, load_success);
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" checking wgl extensions...\n");
|
||||
char *extensions_c = wglGetExtensionsStringEXT();
|
||||
String_Const_u8 extensions = SCu8((u8*)extensions_c);
|
||||
|
||||
{
|
||||
String_Const_u8 s = string_skip_whitespace(extensions);
|
||||
for (;s.size > 0;){
|
||||
u64 end = string_find_first_whitespace(s);
|
||||
String_Const_u8 m = string_prefix(s, end);
|
||||
if (string_match(m, string_u8_litexpr("WGL_EXT_framebuffer_sRGB")) ||
|
||||
string_match(m, string_u8_litexpr("WGL_ARB_framebuffer_sRGB"))){
|
||||
srgb_support = true;
|
||||
}
|
||||
else if (string_match(m, string_u8_litexpr("WGL_EXT_swap_interval"))){
|
||||
b32 wgl_swap_interval_ext = true;
|
||||
LoadWGL(wglSwapIntervalEXT, wgl_swap_interval_ext);
|
||||
if (!wgl_swap_interval_ext){
|
||||
wglSwapIntervalEXT = 0;
|
||||
}
|
||||
}
|
||||
s = string_skip_whitespace(string_skip(s, end));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(allen): Load gl functions
|
||||
log_os(" loading core GL functions...\n");
|
||||
|
||||
#define GL_FUNC(f,R,P) LoadWGL(f,load_success);
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Cleanup the GL bootstrap resources
|
||||
log_os(" cleaning up boostrap resources...\n");
|
||||
|
||||
ReleaseDC(wglwindow, wgldc);
|
||||
DestroyWindow(wglwindow);
|
||||
wglDeleteContext(wglcontext);
|
||||
|
||||
// NOTE(allen): Register the graphics window class
|
||||
log_os(" registering graphics class...\n");
|
||||
|
||||
WNDCLASSW wndclass = {};
|
||||
wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
|
||||
wndclass.lpfnWndProc = win32_proc;
|
||||
wndclass.hIcon = LoadIconW(GetModuleHandle(0), L"main");
|
||||
wndclass.hInstance = this_instance;
|
||||
wndclass.lpszClassName = L"GRAPHICS-WINDOW-NAME";
|
||||
if (RegisterClassW(&wndclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
}
|
||||
fail_register:;
|
||||
|
||||
b32 result = false;
|
||||
if (register_success){
|
||||
// NOTE(allen): Create the graphics window
|
||||
log_os(" creating graphics window...\n");
|
||||
|
||||
HWND wnd = CreateWindowExW(0, L"GRAPHICS-WINDOW-NAME", L"GRAPHICS", style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
|
||||
0, 0, this_instance, 0);
|
||||
|
||||
*wnd_out = 0;
|
||||
*context_out = 0;
|
||||
if (wnd != 0){
|
||||
log_os(" setting graphics pixel format...\n");
|
||||
|
||||
HDC dc = GetDC(wnd);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
|
||||
i32 pixel_attrib_list[] = {
|
||||
/* 0*/WGL_DRAW_TO_WINDOW_ARB, TRUE,
|
||||
/* 2*/WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
|
||||
/* 4*/WGL_SUPPORT_OPENGL_ARB, TRUE,
|
||||
/* 6*/WGL_DOUBLE_BUFFER_ARB, false,
|
||||
/* 8*/WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||
/*10*/WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE,
|
||||
/*12*/0,
|
||||
};
|
||||
if (!srgb_support){
|
||||
pixel_attrib_list[10] = 0;
|
||||
}
|
||||
|
||||
i32 suggested_format_index = 0;
|
||||
u32 ignore = 0;
|
||||
if (!wglChoosePixelFormatARB(dc, pixel_attrib_list, 0, 1, &suggested_format_index, &ignore)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
DescribePixelFormat(dc, suggested_format_index, sizeof(format), &format);
|
||||
if (!SetPixelFormat(dc, suggested_format_index, &format)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" setting graphics attributes...\n");
|
||||
|
||||
i32 context_attrib_list[] = {
|
||||
/*0*/WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
/*2*/WGL_CONTEXT_MINOR_VERSION_ARB, 2,
|
||||
/*4*/WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
|
||||
#if GL_DEBUG_MODE
|
||||
|WGL_CONTEXT_DEBUG_BIT_ARB
|
||||
#endif
|
||||
,
|
||||
/*6*/WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
/*8*/0
|
||||
};
|
||||
|
||||
log_os(" creating graphics GL context...\n");
|
||||
HGLRC context = wglCreateContextAttribsARB(dc, 0, context_attrib_list);
|
||||
if (context == 0){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" making graphics GL context current...\n");
|
||||
wglMakeCurrent(dc, context);
|
||||
|
||||
|
||||
if (wglSwapIntervalEXT != 0){
|
||||
log_os(" setting swap interval...\n");
|
||||
wglSwapIntervalEXT(1);
|
||||
}
|
||||
*wnd_out = wnd;
|
||||
*context_out = context;
|
||||
result = true;
|
||||
|
||||
if (false){
|
||||
fail_window_init:;
|
||||
DWORD error = GetLastError();
|
||||
ReleaseDC(wnd, dc);
|
||||
DestroyWindow(wnd);
|
||||
SetLastError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
//-
|
||||
|
||||
|
@ -1923,10 +1748,16 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
window_rect.bottom - window_rect.top,
|
||||
((window_style & WS_MAXIMIZE) != 0));
|
||||
|
||||
#if defined( WIN32_DIRECTX )
|
||||
if( !win32_gl_create_window( &win32vars.window_handle, window_style, window_rect ) ) {
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
HGLRC window_opengl_context = 0;
|
||||
if (!win32_gl_create_window(&win32vars.window_handle, &window_opengl_context, window_style, window_rect)){
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
log_os(" window created successfully\n");
|
||||
|
||||
|
@ -2027,7 +1858,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
|
||||
// NOTE(allen): while we're doing this (and possibly sleeping)
|
||||
// we can let async processes get there time in.
|
||||
system_release_global_frame_mutex(win32vars.tctx);
|
||||
system_release_global_frame_mutex(win32vars.tctx);
|
||||
|
||||
b32 get_more_messages = true;
|
||||
do{
|
||||
|
@ -2105,7 +1936,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
}
|
||||
}while (get_more_messages);
|
||||
|
||||
system_acquire_global_frame_mutex(win32vars.tctx);
|
||||
system_acquire_global_frame_mutex(win32vars.tctx);
|
||||
}
|
||||
|
||||
// NOTE(allen): Mouse Out of Window Detection
|
||||
|
@ -2210,10 +2041,15 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
win32vars.lctrl_lalt_is_altgr = (b8)result.lctrl_lalt_is_altgr;
|
||||
|
||||
// NOTE(allen): render
|
||||
#if defined( WIN32_DIRECTX )
|
||||
gl_render(&target);
|
||||
g_directx.swap_chain->Present( 1, 0 );
|
||||
#else
|
||||
HDC hdc = GetDC(win32vars.window_handle);
|
||||
gl_render(&target);
|
||||
SwapBuffers(hdc);
|
||||
ReleaseDC(win32vars.window_handle, hdc);
|
||||
#endif
|
||||
|
||||
// NOTE(allen): toggle full screen
|
||||
if (win32vars.do_toggle){
|
||||
|
@ -2230,7 +2066,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
}
|
||||
|
||||
// NOTE(allen): sleep a bit to cool off :)
|
||||
system_release_global_frame_mutex(win32vars.tctx);
|
||||
system_release_global_frame_mutex(win32vars.tctx);
|
||||
|
||||
u64 timer_end = system_now_time();
|
||||
u64 end_target = timer_start + frame_useconds;
|
||||
|
@ -2249,6 +2085,10 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
win32vars.first = false;
|
||||
}
|
||||
|
||||
#if defined( WIN32_DIRECTX ) && !SHIP_MODE
|
||||
win32_gl_cleanup( );
|
||||
#endif
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,491 @@
|
|||
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "d3dcompiler.lib")
|
||||
|
||||
#include <initguid.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <dxgi1_3.h>
|
||||
#include <d3dcompiler.h>
|
||||
|
||||
#if !SHIP_MODE
|
||||
#include <dxgidebug.h>
|
||||
IDXGIDebug1* dxgi_debug;
|
||||
#endif
|
||||
|
||||
#include "4ed_directx_render.cpp"
|
||||
|
||||
internal LRESULT CALL_CONVENTION
|
||||
win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
internal b32
|
||||
win32_gl_create_window(HWND *wnd_out, DWORD style, RECT rect){
|
||||
|
||||
local_persist b32 srgb_support = false;
|
||||
local_persist b32 first_call = true;
|
||||
|
||||
b32 result = false;
|
||||
|
||||
*wnd_out = 0;
|
||||
|
||||
Assert( g_directx.initialized == 0 );
|
||||
Assert( g_directx.device == 0 );
|
||||
Assert( g_directx.context == 0 );
|
||||
Assert( g_directx.swap_chain == 0 );
|
||||
Assert( g_directx.sampler == 0 );
|
||||
Assert( g_directx.render_target_view == 0 );
|
||||
Assert( g_directx.gpu_program.vertex == 0 );
|
||||
Assert( g_directx.gpu_program.layout == 0 );
|
||||
Assert( g_directx.gpu_program.pixel == 0 );
|
||||
Assert( g_directx.gpu_program.valid == 0 );
|
||||
Assert( g_directx.vertex_buffer == 0 );
|
||||
Assert( g_directx.constants_buffer == 0 );
|
||||
Assert( g_directx.texture_count == 0 );
|
||||
|
||||
g_directx = { };
|
||||
|
||||
HINSTANCE this_instance = GetModuleHandle(0);
|
||||
|
||||
HWND wnd = 0;
|
||||
|
||||
ID3D11Device* base_device = 0;
|
||||
ID3D11DeviceContext* base_device_context = 0;
|
||||
IDXGIFactory2* dxgi_factory = 0;
|
||||
|
||||
ID3D11BlendState* blend_state = 0;
|
||||
ID3D11RasterizerState1* rasterizer_state = 0;
|
||||
|
||||
do {
|
||||
|
||||
// NOTE(simon): There is nothing in the code suggesting that this function could be called several time.
|
||||
// If it is called several time, we would need to make sure that we cleaned up previous DirectX resources.
|
||||
// The reason this function would be called twice would be if it failed previously, and in that case we
|
||||
// clean up everythings so we should be good. Still we assume it's only ever call once.
|
||||
Assert( first_call );
|
||||
|
||||
if (first_call){
|
||||
|
||||
first_call = false;
|
||||
|
||||
// NOTE(allen): Register the graphics window class
|
||||
log_os(" registering graphics class...\n");
|
||||
|
||||
WNDCLASSW wndclass = {};
|
||||
wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
|
||||
wndclass.lpfnWndProc = win32_proc;
|
||||
wndclass.hIcon = LoadIconW(GetModuleHandle(0), L"main");
|
||||
wndclass.hInstance = this_instance;
|
||||
wndclass.lpszClassName = L"GRAPHICS-WINDOW-NAME";
|
||||
if (RegisterClassW(&wndclass) == 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(allen): Create the graphics window
|
||||
log_os(" creating graphics window...\n");
|
||||
|
||||
wnd = CreateWindowExW(0, L"GRAPHICS-WINDOW-NAME", L"GRAPHICS", style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
|
||||
0, 0, this_instance, 0);
|
||||
|
||||
if (wnd == 0) {
|
||||
log_os( " Failed to create a window.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE(simon): We are creating a directx 11.1 device and context (supported since windows 8).
|
||||
D3D_FEATURE_LEVEL feature_levels[ ] = { D3D_FEATURE_LEVEL_11_1 };
|
||||
|
||||
u32 device_flags = 0;
|
||||
// device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
||||
|
||||
#if !SHIP_MODE
|
||||
device_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
#endif
|
||||
|
||||
HRESULT hr = D3D11CreateDevice( 0, D3D_DRIVER_TYPE_HARDWARE, 0, device_flags, feature_levels, ArrayCount( feature_levels ), D3D11_SDK_VERSION, &base_device, 0, &base_device_context );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a hardware DirectX device and context.\n" );
|
||||
log_os( " Trying to create a software (WARP) DirectX device.\n" );
|
||||
// NOTE(simon): Try creating a high performance software device as a fallback.
|
||||
hr = D3D11CreateDevice( 0, D3D_DRIVER_TYPE_WARP, 0, device_flags, feature_levels, ArrayCount( feature_levels ), D3D11_SDK_VERSION, &base_device, 0, &base_device_context );
|
||||
}
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create software (WARP) DirectX device and context.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
hr = base_device->QueryInterface( __uuidof( ID3D11Device1 ), ( void** ) &g_directx.device );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a ID3D11Device1.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
hr = base_device_context->QueryInterface( __uuidof( ID3D11DeviceContext1 ), ( void** ) &g_directx.context );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a ID3D11DeviceContext1.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
ID3D11Device1* device = g_directx.device;
|
||||
ID3D11DeviceContext1* context = g_directx.context;
|
||||
|
||||
#if !SHIP_MODE
|
||||
ID3D11InfoQueue* info;
|
||||
hr = device->QueryInterface( __uuidof( ID3D11InfoQueue ), ( void** ) &info );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to get ID3D11InfoQueue. This is not important if you're not debugging graphics.\n" );
|
||||
} else {
|
||||
info->SetBreakOnSeverity( D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE );
|
||||
info->SetBreakOnSeverity( D3D11_MESSAGE_SEVERITY_ERROR, TRUE );
|
||||
info->Release( );
|
||||
}
|
||||
|
||||
hr = DXGIGetDebugInterface1( 0, __uuidof( IDXGIDebug1 ), ( void** ) &dxgi_debug );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to get IDXGIDebug1. This is not important if you're not debugging graphics.\n" );
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOTE(simon): sRGB should be supported by any hardware now, but there was a check so I added one.
|
||||
DXGI_FORMAT back_buffer_format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
UINT format_support = 0;
|
||||
|
||||
if ( SUCCEEDED( device->CheckFormatSupport( back_buffer_format, &format_support ) ) ) {
|
||||
u32 required = D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_DISPLAY;
|
||||
srgb_support = ( ( format_support & required ) == required );
|
||||
}
|
||||
|
||||
if ( !srgb_support ) {
|
||||
log_os( " sRBG back buffer not supported.\n" );
|
||||
back_buffer_format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
} else {
|
||||
log_os( " sRBG back buffer supported.\n" );
|
||||
}
|
||||
|
||||
hr = CreateDXGIFactory( __uuidof( IDXGIFactory2 ), ( void** ) &dxgi_factory );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create IDXGIFactory2.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { };
|
||||
// NOTE(simon): Can't request sRGB format here when using FLIP_* swap chain. It's requested we creating the render target view.
|
||||
// NOTE(simon): 4coder never calls glEnable( GL_FRAMEBUFFER_SRGB ) so we don't enable sRGB.
|
||||
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swap_chain_desc.SampleDesc.Count = 1;
|
||||
swap_chain_desc.SampleDesc.Quality = 0;
|
||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swap_chain_desc.BufferCount = 2;
|
||||
swap_chain_desc.Scaling = DXGI_SCALING_NONE;
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
|
||||
hr = dxgi_factory->CreateSwapChainForHwnd( device, wnd, &swap_chain_desc, 0, 0, &g_directx.swap_chain );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create IDXGISwapChain1.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE(simon): We setup alpha blending here as it's always on in 4coder.
|
||||
D3D11_BLEND_DESC blend_state_desc = { };
|
||||
blend_state_desc.RenderTarget[ 0 ].BlendEnable = TRUE;
|
||||
blend_state_desc.RenderTarget[ 0 ].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].BlendOp = D3D11_BLEND_OP_ADD;
|
||||
blend_state_desc.RenderTarget[ 0 ].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
blend_state_desc.RenderTarget[ 0 ].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
|
||||
hr = device->CreateBlendState( &blend_state_desc, &blend_state );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a blend state.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Setting blend state...\n" );
|
||||
context->OMSetBlendState( blend_state, 0, 0xffffffff );
|
||||
|
||||
// NOTE(simon): Enable scissor and disable backface culling.
|
||||
D3D11_RASTERIZER_DESC1 rasterizer_desc = { };
|
||||
rasterizer_desc.FillMode = D3D11_FILL_SOLID;
|
||||
rasterizer_desc.CullMode = D3D11_CULL_NONE;
|
||||
rasterizer_desc.DepthClipEnable = TRUE;
|
||||
rasterizer_desc.ScissorEnable = TRUE;
|
||||
|
||||
hr = device->CreateRasterizerState1( &rasterizer_desc, &rasterizer_state );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a rasterizer state.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Setting rasterizer state...\n" );
|
||||
context->RSSetState( rasterizer_state );
|
||||
|
||||
// NOTE(simon): Not setting depth stencil as 4coder doesn't use it.
|
||||
// NOTE(simon): Swap interval is a parameter of swap_chain->present.
|
||||
|
||||
D3D11_SAMPLER_DESC linear_desc = { };
|
||||
linear_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
linear_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
linear_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
linear_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
linear_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
||||
linear_desc.MinLOD = 0;
|
||||
linear_desc.MaxLOD = D3D11_FLOAT32_MAX;
|
||||
|
||||
hr = device->CreateSamplerState( &linear_desc, &g_directx.sampler );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a sampler state.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE(simon): We create the vertex buffer, constants buffers and shader here because if
|
||||
// we can't create them we won't be able to render anything and so we should just exit the program.
|
||||
|
||||
D3D11_BUFFER_DESC vertex_buffer_desc = { };
|
||||
// NOTE(simon): Reserving 400K vertices which is about 11 megabytes and would allow 100K characters.
|
||||
// On a 1080p monitor, with 4 by 10 pixels characters we would need
|
||||
// (1920/4)*(1080/10) = 51840 characters to fill the screen.
|
||||
vertex_buffer_desc.ByteWidth = 400000 * sizeof( Render_Vertex );
|
||||
vertex_buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
vertex_buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
vertex_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
hr = device->CreateBuffer( &vertex_buffer_desc, 0, &g_directx.vertex_buffer );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a vertex buffer.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
D3D11_BUFFER_DESC constants_buffer_desc = { };
|
||||
// NOTE(simon): constants buffer size needs to be a multiple of 16.
|
||||
// NOTE(simon): The layout is explained where we set the values in the buffer in gl_render.
|
||||
constants_buffer_desc.ByteWidth = 32;
|
||||
constants_buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
constants_buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
constants_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
hr = device->CreateBuffer( &constants_buffer_desc, 0, &g_directx.constants_buffer );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed to create a constants buffer.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
g_directx.gpu_program = gl__make_program( gl__vertex, gl__fragment );
|
||||
|
||||
if ( !g_directx.gpu_program.valid ) {
|
||||
break;
|
||||
}
|
||||
|
||||
*wnd_out = wnd;
|
||||
g_directx.texture_count = 1; // NOTE(simon): Reserve the first texture slot as a invalid/unbind texture.
|
||||
g_directx.initialized = true;
|
||||
result = true;
|
||||
|
||||
} while ( 0 );
|
||||
|
||||
if ( !result ) {
|
||||
|
||||
if ( wnd ) {
|
||||
DestroyWindow( wnd );
|
||||
( *wnd_out ) = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.context ) {
|
||||
g_directx.context->OMSetBlendState( 0, 0, 0xffffffff );
|
||||
g_directx.context->RSSetState( 0 );
|
||||
}
|
||||
|
||||
if ( g_directx.constants_buffer ) {
|
||||
g_directx.constants_buffer->Release( );
|
||||
g_directx.constants_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.vertex_buffer ) {
|
||||
g_directx.vertex_buffer->Release( );
|
||||
g_directx.vertex_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.gpu_program.valid ) {
|
||||
|
||||
if ( g_directx.gpu_program.vertex ) {
|
||||
g_directx.gpu_program.vertex->Release( );
|
||||
g_directx.gpu_program.vertex = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.gpu_program.layout ) {
|
||||
g_directx.gpu_program.layout->Release( );
|
||||
g_directx.gpu_program.layout = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.gpu_program.pixel ) {
|
||||
g_directx.gpu_program.pixel->Release( );
|
||||
g_directx.gpu_program.pixel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(simon): No render target view at this point as it's created in the WM_SIZE message.
|
||||
|
||||
if ( g_directx.sampler ) {
|
||||
g_directx.sampler->Release( );
|
||||
g_directx.sampler = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.swap_chain ) {
|
||||
g_directx.swap_chain->Release( );
|
||||
g_directx.swap_chain = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.context ) {
|
||||
g_directx.context->Release( );
|
||||
g_directx.context = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.device ) {
|
||||
g_directx.device->Release( );
|
||||
g_directx.device = 0;
|
||||
}
|
||||
|
||||
g_directx.initialized = false;
|
||||
|
||||
#if SHIP_MODE
|
||||
os_popup_error( "Error", "Window creation failed.");
|
||||
#endif
|
||||
}
|
||||
|
||||
if( base_device ) {
|
||||
base_device->Release( );
|
||||
base_device = 0;
|
||||
}
|
||||
|
||||
if ( base_device_context ) {
|
||||
base_device_context->Release( );
|
||||
base_device_context = 0;
|
||||
}
|
||||
|
||||
if ( dxgi_factory ) {
|
||||
dxgi_factory->Release( );
|
||||
dxgi_factory = 0;
|
||||
}
|
||||
|
||||
if ( blend_state ) {
|
||||
blend_state->Release( );
|
||||
blend_state = 0;
|
||||
}
|
||||
|
||||
if ( rasterizer_state ) {
|
||||
rasterizer_state->Release( );
|
||||
rasterizer_state = 0;
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
#if !SHIP_MODE
|
||||
|
||||
// NOTE(simon): Only call this when working on 4coder, to make sure we don't do something stupid.
|
||||
// In SHIP_MODE we let the os clean up resources.
|
||||
internal void
|
||||
win32_gl_cleanup( void ) {
|
||||
|
||||
if ( dxgi_debug && g_directx.initialized ) {
|
||||
|
||||
OutputDebugString( L"win32_gl_cleanup start report...\n" );
|
||||
dxgi_debug->ReportLiveObjects( DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL );
|
||||
|
||||
g_directx.initialized = false;
|
||||
|
||||
if ( g_directx.context ) {
|
||||
g_directx.context->OMSetBlendState( 0, 0, 0xffffffff );
|
||||
g_directx.context->RSSetState( 0 );
|
||||
}
|
||||
|
||||
for ( u32 i = 1; i < g_directx.texture_count; i++ ) {
|
||||
|
||||
DirectXTexture* texture = g_directx.textures + i;
|
||||
|
||||
if ( texture->view ) {
|
||||
texture->view->Release( );
|
||||
texture->view = 0;
|
||||
}
|
||||
|
||||
if ( texture->pointer ) {
|
||||
texture->pointer->Release( );
|
||||
texture->pointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_directx.texture_count = 0;
|
||||
|
||||
if ( g_directx.constants_buffer ) {
|
||||
g_directx.constants_buffer->Release( );
|
||||
g_directx.constants_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.vertex_buffer ) {
|
||||
g_directx.vertex_buffer->Release( );
|
||||
g_directx.vertex_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.gpu_program.valid ) {
|
||||
|
||||
if ( g_directx.gpu_program.vertex ) {
|
||||
g_directx.gpu_program.vertex->Release( );
|
||||
g_directx.gpu_program.vertex = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.gpu_program.layout ) {
|
||||
g_directx.gpu_program.layout->Release( );
|
||||
g_directx.gpu_program.layout = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.gpu_program.pixel ) {
|
||||
g_directx.gpu_program.pixel->Release( );
|
||||
g_directx.gpu_program.pixel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( g_directx.render_target_view ) {
|
||||
g_directx.render_target_view->Release( );
|
||||
g_directx.render_target_view = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.sampler ) {
|
||||
g_directx.sampler->Release( );
|
||||
g_directx.sampler = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.swap_chain ) {
|
||||
g_directx.swap_chain->Release( );
|
||||
g_directx.swap_chain = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.context ) {
|
||||
g_directx.context->Release( );
|
||||
g_directx.context = 0;
|
||||
}
|
||||
|
||||
if ( g_directx.device ) {
|
||||
g_directx.device->Release( );
|
||||
g_directx.device = 0;
|
||||
}
|
||||
|
||||
OutputDebugString( L"win32_gl_cleanup end report (nothing should be printed after this line)...\n" );
|
||||
dxgi_debug->ReportLiveObjects( DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL );
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,257 @@
|
|||
|
||||
#pragma comment(lib, "OpenGL32.lib")
|
||||
|
||||
#include "win32_gl.h"
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include "opengl/4ed_opengl_defines.h"
|
||||
#define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0;
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
#include "opengl/4ed_opengl_render.cpp"
|
||||
|
||||
internal b32
|
||||
win32_wgl_good(Void_Func *f){
|
||||
return(f != 0 &&
|
||||
f != (Void_Func*)1 &&
|
||||
f != (Void_Func*)2 &&
|
||||
f != (Void_Func*)3 &&
|
||||
f != (Void_Func*)-1);
|
||||
}
|
||||
|
||||
typedef HGLRC (CALL_CONVENTION wglCreateContextAttribsARB_Function)(HDC,HGLRC,i32*);
|
||||
typedef BOOL (CALL_CONVENTION wglChoosePixelFormatARB_Function)(HDC,i32*,f32*,u32,i32*,u32*);
|
||||
typedef char* (CALL_CONVENTION wglGetExtensionsStringEXT_Function)();
|
||||
typedef VOID (CALL_CONVENTION wglSwapIntervalEXT_Function)(i32);
|
||||
|
||||
global wglCreateContextAttribsARB_Function *wglCreateContextAttribsARB = 0;
|
||||
global wglChoosePixelFormatARB_Function *wglChoosePixelFormatARB = 0;
|
||||
global wglGetExtensionsStringEXT_Function *wglGetExtensionsStringEXT = 0;
|
||||
global wglSwapIntervalEXT_Function *wglSwapIntervalEXT = 0;
|
||||
|
||||
internal LRESULT CALL_CONVENTION
|
||||
win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
internal b32
|
||||
win32_gl_create_window(HWND *wnd_out, HGLRC *context_out, DWORD style, RECT rect){
|
||||
HINSTANCE this_instance = GetModuleHandle(0);
|
||||
|
||||
local_persist b32 srgb_support = false;
|
||||
local_persist b32 register_success = true;
|
||||
local_persist b32 first_call = true;
|
||||
if (first_call){
|
||||
log_os(" GL bootstrapping...\n");
|
||||
|
||||
first_call = false;
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap window
|
||||
log_os(" registering bootstrap class...\n");
|
||||
WNDCLASSW wglclass = {};
|
||||
wglclass.lpfnWndProc = DefWindowProcW;
|
||||
wglclass.hInstance = this_instance;
|
||||
wglclass.lpszClassName = L"wgl-loader";
|
||||
if (RegisterClassW(&wglclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap window...\n");
|
||||
HWND wglwindow = CreateWindowW(wglclass.lpszClassName, L"", 0, 0, 0, 0, 0,
|
||||
0, 0, this_instance, 0);
|
||||
if (wglwindow == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap context
|
||||
log_os(" setting bootstrap pixel format...\n");
|
||||
|
||||
HDC wgldc = GetDC(wglwindow);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
format.nSize = sizeof(format);
|
||||
format.nVersion = 1;
|
||||
format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
|
||||
format.iPixelType = PFD_TYPE_RGBA;
|
||||
format.cColorBits = 32;
|
||||
format.cAlphaBits = 8;
|
||||
format.cDepthBits = 24;
|
||||
format.iLayerType = PFD_MAIN_PLANE;
|
||||
i32 suggested_format_index = ChoosePixelFormat(wgldc, &format);
|
||||
if (!SetPixelFormat(wgldc, suggested_format_index, &format)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap GL context...\n");
|
||||
HGLRC wglcontext = wglCreateContext(wgldc);
|
||||
if (wglcontext == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" making bootstrap GL context current...\n");
|
||||
if (!wglMakeCurrent(wgldc, wglcontext)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Load wgl extensions
|
||||
log_os(" loading wgl extensions...\n");
|
||||
|
||||
#define LoadWGL(f,l) Stmnt((f) = (f##_Function*)wglGetProcAddress(#f); \
|
||||
(l) = (l) && win32_wgl_good((Void_Func*)(f));)
|
||||
|
||||
b32 load_success = true;
|
||||
LoadWGL(wglCreateContextAttribsARB, load_success);
|
||||
LoadWGL(wglChoosePixelFormatARB, load_success);
|
||||
LoadWGL(wglGetExtensionsStringEXT, load_success);
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" checking wgl extensions...\n");
|
||||
char *extensions_c = wglGetExtensionsStringEXT();
|
||||
String_Const_u8 extensions = SCu8((u8*)extensions_c);
|
||||
|
||||
{
|
||||
String_Const_u8 s = string_skip_whitespace(extensions);
|
||||
for (;s.size > 0;){
|
||||
u64 end = string_find_first_whitespace(s);
|
||||
String_Const_u8 m = string_prefix(s, end);
|
||||
if (string_match(m, string_u8_litexpr("WGL_EXT_framebuffer_sRGB")) ||
|
||||
string_match(m, string_u8_litexpr("WGL_ARB_framebuffer_sRGB"))){
|
||||
srgb_support = true;
|
||||
}
|
||||
else if (string_match(m, string_u8_litexpr("WGL_EXT_swap_interval"))){
|
||||
b32 wgl_swap_interval_ext = true;
|
||||
LoadWGL(wglSwapIntervalEXT, wgl_swap_interval_ext);
|
||||
if (!wgl_swap_interval_ext){
|
||||
wglSwapIntervalEXT = 0;
|
||||
}
|
||||
}
|
||||
s = string_skip_whitespace(string_skip(s, end));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(allen): Load gl functions
|
||||
log_os(" loading core GL functions...\n");
|
||||
|
||||
#define GL_FUNC(f,R,P) LoadWGL(f,load_success);
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Cleanup the GL bootstrap resources
|
||||
log_os(" cleaning up boostrap resources...\n");
|
||||
|
||||
ReleaseDC(wglwindow, wgldc);
|
||||
DestroyWindow(wglwindow);
|
||||
wglDeleteContext(wglcontext);
|
||||
|
||||
// NOTE(allen): Register the graphics window class
|
||||
log_os(" registering graphics class...\n");
|
||||
|
||||
WNDCLASSW wndclass = {};
|
||||
wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
|
||||
wndclass.lpfnWndProc = win32_proc;
|
||||
wndclass.hIcon = LoadIconW(GetModuleHandle(0), L"main");
|
||||
wndclass.hInstance = this_instance;
|
||||
wndclass.lpszClassName = L"GRAPHICS-WINDOW-NAME";
|
||||
if (RegisterClassW(&wndclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
}
|
||||
fail_register:;
|
||||
|
||||
b32 result = false;
|
||||
if (register_success){
|
||||
// NOTE(allen): Create the graphics window
|
||||
log_os(" creating graphics window...\n");
|
||||
|
||||
HWND wnd = CreateWindowExW(0, L"GRAPHICS-WINDOW-NAME", L"GRAPHICS", style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
|
||||
0, 0, this_instance, 0);
|
||||
|
||||
*wnd_out = 0;
|
||||
*context_out = 0;
|
||||
if (wnd != 0){
|
||||
log_os(" setting graphics pixel format...\n");
|
||||
|
||||
HDC dc = GetDC(wnd);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
|
||||
i32 pixel_attrib_list[] = {
|
||||
/* 0*/WGL_DRAW_TO_WINDOW_ARB, TRUE,
|
||||
/* 2*/WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
|
||||
/* 4*/WGL_SUPPORT_OPENGL_ARB, TRUE,
|
||||
/* 6*/WGL_DOUBLE_BUFFER_ARB, false,
|
||||
/* 8*/WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||
/*10*/WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE,
|
||||
/*12*/0,
|
||||
};
|
||||
if (!srgb_support){
|
||||
pixel_attrib_list[10] = 0;
|
||||
}
|
||||
|
||||
i32 suggested_format_index = 0;
|
||||
u32 ignore = 0;
|
||||
if (!wglChoosePixelFormatARB(dc, pixel_attrib_list, 0, 1, &suggested_format_index, &ignore)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
DescribePixelFormat(dc, suggested_format_index, sizeof(format), &format);
|
||||
if (!SetPixelFormat(dc, suggested_format_index, &format)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" setting graphics attributes...\n");
|
||||
|
||||
i32 context_attrib_list[] = {
|
||||
/*0*/WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
/*2*/WGL_CONTEXT_MINOR_VERSION_ARB, 2,
|
||||
/*4*/WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
|
||||
#if GL_DEBUG_MODE
|
||||
|WGL_CONTEXT_DEBUG_BIT_ARB
|
||||
#endif
|
||||
,
|
||||
/*6*/WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
/*8*/0
|
||||
};
|
||||
|
||||
log_os(" creating graphics GL context...\n");
|
||||
HGLRC context = wglCreateContextAttribsARB(dc, 0, context_attrib_list);
|
||||
if (context == 0){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" making graphics GL context current...\n");
|
||||
wglMakeCurrent(dc, context);
|
||||
|
||||
|
||||
if (wglSwapIntervalEXT != 0){
|
||||
log_os(" setting swap interval...\n");
|
||||
wglSwapIntervalEXT(1);
|
||||
}
|
||||
*wnd_out = wnd;
|
||||
*context_out = context;
|
||||
result = true;
|
||||
|
||||
if (false){
|
||||
fail_window_init:;
|
||||
DWORD error = GetLastError();
|
||||
ReleaseDC(wnd, dc);
|
||||
DestroyWindow(wnd);
|
||||
SetLastError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
Loading…
Reference in New Issue