/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2014 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include #include #include #include using namespace xe; using namespace xe::gpu; using namespace xe::gpu::d3d11; namespace { const uint8_t profiler_font[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x10,0x78,0x38,0x78,0x7c,0x7c,0x3c,0x44,0x38,0x04,0x44,0x40,0x44,0x44,0x38,0x78, 0x38,0x78,0x38,0x7c,0x44,0x44,0x44,0x44,0x44,0x7c,0x00,0x00,0x40,0x00,0x04,0x00, 0x18,0x00,0x40,0x10,0x08,0x40,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x10,0x38,0x7c,0x08,0x7c,0x1c,0x7c,0x38,0x38, 0x10,0x28,0x28,0x10,0x00,0x20,0x10,0x08,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x04,0x00,0x20,0x38,0x38,0x70,0x00,0x1c,0x10,0x00,0x1c,0x10,0x70,0x30,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x28,0x44,0x44,0x44,0x40,0x40,0x40,0x44,0x10,0x04,0x48,0x40,0x6c,0x44,0x44,0x44, 0x44,0x44,0x44,0x10,0x44,0x44,0x44,0x44,0x44,0x04,0x00,0x00,0x40,0x00,0x04,0x00, 0x24,0x00,0x40,0x00,0x00,0x40,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x30,0x44,0x04,0x18,0x40,0x20,0x04,0x44,0x44, 0x10,0x28,0x28,0x3c,0x44,0x50,0x10,0x10,0x08,0x54,0x10,0x00,0x00,0x00,0x04,0x00, 0x00,0x08,0x00,0x10,0x44,0x44,0x40,0x40,0x04,0x28,0x00,0x30,0x10,0x18,0x58,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x44,0x44,0x40,0x44,0x40,0x40,0x40,0x44,0x10,0x04,0x50,0x40,0x54,0x64,0x44,0x44, 0x44,0x44,0x40,0x10,0x44,0x44,0x44,0x28,0x28,0x08,0x00,0x38,0x78,0x3c,0x3c,0x38, 0x20,0x38,0x78,0x30,0x18,0x44,0x10,0x6c,0x78,0x38,0x78,0x3c,0x5c,0x3c,0x3c,0x44, 0x44,0x44,0x44,0x44,0x7c,0x00,0x4c,0x10,0x04,0x08,0x28,0x78,0x40,0x08,0x44,0x44, 0x10,0x00,0x7c,0x50,0x08,0x50,0x00,0x20,0x04,0x38,0x10,0x00,0x00,0x00,0x08,0x10, 0x10,0x10,0x7c,0x08,0x08,0x54,0x40,0x20,0x04,0x44,0x00,0x30,0x10,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x44,0x78,0x40,0x44,0x78,0x78,0x40,0x7c,0x10,0x04,0x60,0x40,0x54,0x54,0x44,0x78, 0x44,0x78,0x38,0x10,0x44,0x44,0x54,0x10,0x10,0x10,0x00,0x04,0x44,0x40,0x44,0x44, 0x78,0x44,0x44,0x10,0x08,0x48,0x10,0x54,0x44,0x44,0x44,0x44,0x60,0x40,0x10,0x44, 0x44,0x44,0x28,0x44,0x08,0x00,0x54,0x10,0x18,0x18,0x48,0x04,0x78,0x10,0x38,0x3c, 0x10,0x00,0x28,0x38,0x10,0x20,0x00,0x20,0x04,0x10,0x7c,0x00,0x7c,0x00,0x10,0x00, 0x00,0x20,0x00,0x04,0x10,0x5c,0x40,0x10,0x04,0x00,0x00,0x60,0x10,0x0c,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x7c,0x44,0x40,0x44,0x40,0x40,0x4c,0x44,0x10,0x04,0x50,0x40,0x44,0x4c,0x44,0x40, 0x54,0x50,0x04,0x10,0x44,0x44,0x54,0x28,0x10,0x20,0x00,0x3c,0x44,0x40,0x44,0x7c, 0x20,0x44,0x44,0x10,0x08,0x70,0x10,0x54,0x44,0x44,0x44,0x44,0x40,0x38,0x10,0x44, 0x44,0x54,0x10,0x44,0x10,0x00,0x64,0x10,0x20,0x04,0x7c,0x04,0x44,0x20,0x44,0x04, 0x10,0x00,0x7c,0x14,0x20,0x54,0x00,0x20,0x04,0x38,0x10,0x10,0x00,0x00,0x20,0x10, 0x10,0x10,0x7c,0x08,0x10,0x58,0x40,0x08,0x04,0x00,0x00,0x30,0x10,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x44,0x44,0x44,0x44,0x40,0x40,0x44,0x44,0x10,0x44,0x48,0x40,0x44,0x44,0x44,0x40, 0x48,0x48,0x44,0x10,0x44,0x28,0x6c,0x44,0x10,0x40,0x00,0x44,0x44,0x40,0x44,0x40, 0x20,0x3c,0x44,0x10,0x08,0x48,0x10,0x54,0x44,0x44,0x44,0x44,0x40,0x04,0x12,0x4c, 0x28,0x54,0x28,0x3c,0x20,0x00,0x44,0x10,0x40,0x44,0x08,0x44,0x44,0x20,0x44,0x08, 0x00,0x00,0x28,0x78,0x44,0x48,0x00,0x10,0x08,0x54,0x10,0x10,0x00,0x00,0x40,0x00, 0x10,0x08,0x00,0x10,0x00,0x40,0x40,0x04,0x04,0x00,0x00,0x30,0x10,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x44,0x78,0x38,0x78,0x7c,0x40,0x3c,0x44,0x38,0x38,0x44,0x7c,0x44,0x44,0x38,0x40, 0x34,0x44,0x38,0x10,0x38,0x10,0x44,0x44,0x10,0x7c,0x00,0x3c,0x78,0x3c,0x3c,0x3c, 0x20,0x04,0x44,0x38,0x48,0x44,0x38,0x44,0x44,0x38,0x78,0x3c,0x40,0x78,0x0c,0x34, 0x10,0x6c,0x44,0x04,0x7c,0x00,0x38,0x38,0x7c,0x38,0x08,0x38,0x38,0x20,0x38,0x70, 0x10,0x00,0x28,0x10,0x00,0x34,0x00,0x08,0x10,0x10,0x00,0x20,0x00,0x10,0x00,0x00, 0x20,0x04,0x00,0x20,0x10,0x3c,0x70,0x00,0x1c,0x00,0x7c,0x1c,0x10,0x70,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x38,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x40,0x04,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; const char* shader_code = " \ cbuffer MatrixBuffer {\n \ float4x4 projection_matrix;\n \ };\n \ Texture2D texture0;\n \ SamplerState sampler0;\n \ struct Vertex {\n \ float2 position : POSITION0;\n \ float2 tex : TEXCOORD0;\n \ float4 color : COLOR0;\n \ };\n \ struct Pixel {\n \ float4 position : SV_POSITION;\n \ float2 tex : TEXCOORD0;\n \ float4 color : COLOR0;\n \ };\n \ Pixel vs(Vertex v) {\n \ Pixel p;\n \ p.position = float4(mul(float4(v.position, 0.0f, 1.0f), projection_matrix).xy - float2(1.0f, -1.0f), 0.0f, 1.0f);\n \ p.tex = v.tex;\n \ p.color = v.color;\n \ return p;\n \ }\n \ float4 ps(Pixel p) : SV_TARGET {\n \ if (p.tex.x > 1.0f) {\n \ return float4(p.color.rgb, 0.5f);\n \ } else {\n \ float4 sample = texture0.Sample(sampler0, p.tex);\n \ if(sample.w < 0.5f) {\n \ discard;\n \ }\n \ return p.color * sample;\n \ }\n \ }\n"; } // namespace D3D11ProfilerDisplay::D3D11ProfilerDisplay(D3D11Window* window) : window_(window) { draw_state_ = { 0 }; if (!SetupState() || !SetupShaders() || !SetupFont()) { // Hrm. assert_always(); } // Pass through mouse events. window->mouse_down.AddListener([](xe::ui::MouseEvent& e) { Profiler::OnMouseDown( e.button() == xe::ui::MouseEvent::MOUSE_BUTTON_LEFT, e.button() == xe::ui::MouseEvent::MOUSE_BUTTON_RIGHT); }); window->mouse_up.AddListener([](xe::ui::MouseEvent& e) { Profiler::OnMouseUp(); }); window->mouse_move.AddListener([](xe::ui::MouseEvent& e) { Profiler::OnMouseMove(e.x(), e.y()); }); window->mouse_wheel.AddListener([](xe::ui::MouseEvent& e) { Profiler::OnMouseWheel(e.x(), e.y(), -e.dy()); }); // Watch for toggle/mode keys and such. window->key_down.AddListener([](xe::ui::KeyEvent& e) { Profiler::OnKeyDown(e.key_code()); }); window->key_up.AddListener([](xe::ui::KeyEvent& e) { Profiler::OnKeyUp(e.key_code()); }); } bool D3D11ProfilerDisplay::SetupState() { HRESULT hr; auto device = window_->device(); D3D11_BLEND_DESC blend_desc; xe_zero_struct(&blend_desc, sizeof(blend_desc)); blend_desc.RenderTarget[0].BlendEnable = true; blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO; blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; blend_desc.RenderTarget[0].RenderTargetWriteMask = 0x0F; hr = device->CreateBlendState(&blend_desc, &blend_state_); assert_true(SUCCEEDED(hr)); D3D11_DEPTH_STENCIL_DESC depth_stencil_desc; xe_zero_struct(&depth_stencil_desc, sizeof(depth_stencil_desc)); depth_stencil_desc.DepthEnable = false; depth_stencil_desc.StencilEnable = false; depth_stencil_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; hr = device->CreateDepthStencilState(&depth_stencil_desc, &depth_stencil_state_); assert_true(SUCCEEDED(hr)); return true; } bool D3D11ProfilerDisplay::SetupShaders() { HRESULT hr; auto device = window_->device(); ID3DBlob* vs_code_blob = nullptr; ID3DBlob* vs_errors = nullptr; hr = D3DCompile( shader_code, xestrlena(shader_code), "D3D11ProfilerDisplay.vs", nullptr, nullptr, "vs", "vs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, &vs_code_blob, &vs_errors); if (FAILED(hr)) { XELOGE("Failed to compile profiler vs: %s", reinterpret_cast(vs_errors->GetBufferPointer())); return false; } hr = device->CreateVertexShader(vs_code_blob->GetBufferPointer(), vs_code_blob->GetBufferSize(), nullptr, &vertex_shader_); if (FAILED(hr)) { XELOGE("Failed to create profiler vs"); return false; } ID3DBlob* ps_code_blob = nullptr; ID3DBlob* ps_errors = nullptr; hr = D3DCompile( shader_code, xestrlena(shader_code), "D3D11ProfilerDisplay.ps", nullptr, nullptr, "ps", "ps_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, &ps_code_blob, &ps_errors); if (FAILED(hr)) { XELOGE("Failed to compile profiler ps: %s", reinterpret_cast(ps_errors->GetBufferPointer())); return false; } hr = device->CreatePixelShader(ps_code_blob->GetBufferPointer(), ps_code_blob->GetBufferSize(), nullptr, &pixel_shader_); if (FAILED(hr)) { XELOGE("Failed to create profiler ps"); return false; } D3D11_BUFFER_DESC buffer_desc = { 0 }; buffer_desc.Usage = D3D11_USAGE_DYNAMIC; buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; buffer_desc.ByteWidth = sizeof(float) * 16; hr = device->CreateBuffer(&buffer_desc, nullptr, &shader_constants_); if (FAILED(hr)) { XELOGE("Failed to create profiler constant buffer"); return false; } D3D11_INPUT_ELEMENT_DESC element_descs[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0, }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0, }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0, }, }; hr = device->CreateInputLayout(element_descs, (UINT)XECOUNT(element_descs), vs_code_blob->GetBufferPointer(), vs_code_blob->GetBufferSize(), &shader_layout_); if (FAILED(hr)) { XELOGE("Failed to create profiler input layout"); return false; } buffer_desc.Usage = D3D11_USAGE_DYNAMIC; buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; buffer_desc.ByteWidth = sizeof(draw_state_.vertex_buffer); hr = device->CreateBuffer(&buffer_desc, nullptr, &vertex_buffer_); if (FAILED(hr)) { XELOGE("Failed to create profiler vertex buffer"); return false; } return true; } bool D3D11ProfilerDisplay::SetupFont() { HRESULT hr; auto device = window_->device(); // Setup font lookup table. for (uint32_t i = 0; i < XECOUNT(font_description_.char_offsets); ++i) { font_description_.char_offsets[i] = 206; } for (uint32_t i = 'A'; i <= 'Z'; ++i) { font_description_.char_offsets[i] = (i-'A')*8+1; } for (uint32_t i = 'a'; i <= 'z'; ++i) { font_description_.char_offsets[i] = (i-'a')*8+217; } for (uint32_t i = '0'; i <= '9'; ++i) { font_description_.char_offsets[i] = (i-'0')*8+433; } for (uint32_t i = '!'; i <= '/'; ++i) { font_description_.char_offsets[i] = (i-'!')*8+513; } for (uint32_t i = ':'; i <= '@'; ++i) { font_description_.char_offsets[i] = (i-':')*8+625+8; } for (uint32_t i = '['; i <= '_'; ++i) { font_description_.char_offsets[i] = (i-'[')*8+681+8; } for (uint32_t i = '{'; i <= '~'; ++i) { font_description_.char_offsets[i] = (i-'{')*8+721+8; } // Unpack font bitmap into an RGBA texture. const int FONT_TEX_X = 1024; const int FONT_TEX_Y = 9; const int UNPACKED_SIZE = FONT_TEX_X * FONT_TEX_Y * 4; uint32_t unpacked[UNPACKED_SIZE]; int idx = 0; int end = FONT_TEX_X * FONT_TEX_Y / 8; for (int i = 0; i < end; i++) { uint8_t b = profiler_font[i]; for (int j = 0; j < 8; ++j) { unpacked[idx++] = b & 0x80 ? 0xFFFFFFFFu : 0; b <<= 1; } } D3D11_TEXTURE2D_DESC texture_desc = { 0 }; texture_desc.Width = FONT_TEX_X; texture_desc.Height = FONT_TEX_Y; texture_desc.MipLevels = 1; texture_desc.ArraySize = 1; texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; texture_desc.SampleDesc.Count = 1; texture_desc.SampleDesc.Quality = 0; texture_desc.Usage = D3D11_USAGE_IMMUTABLE; texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; texture_desc.CPUAccessFlags = 0; texture_desc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA initial_data = { 0 }; initial_data.pSysMem = unpacked; initial_data.SysMemPitch = FONT_TEX_X * 4; initial_data.SysMemSlicePitch = 0; ID3D11Texture2D* font_texture = nullptr; hr = device->CreateTexture2D(&texture_desc, &initial_data, &font_texture); if (FAILED(hr)) { XELOGE("Unable to create profiler font texture"); return false; } D3D11_SHADER_RESOURCE_VIEW_DESC texture_view_desc; xe_zero_struct(&texture_view_desc, sizeof(texture_view_desc)); texture_view_desc.Format = texture_desc.Format; texture_view_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; texture_view_desc.Texture2D.MipLevels = 1; texture_view_desc.Texture2D.MostDetailedMip = 0; hr = device->CreateShaderResourceView( font_texture, &texture_view_desc, &font_texture_view_); XESAFERELEASE(font_texture); if (FAILED(hr)) { XELOGE("Unable to create profiler font texture view"); return false; } D3D11_SAMPLER_DESC sampler_desc; xe_zero_struct(&sampler_desc, sizeof(sampler_desc)); sampler_desc.Filter = D3D11_ENCODE_BASIC_FILTER( D3D11_FILTER_TYPE_POINT, D3D11_FILTER_TYPE_POINT, D3D11_FILTER_TYPE_POINT, false); sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.MipLODBias; sampler_desc.MaxAnisotropy = 1; sampler_desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; sampler_desc.BorderColor[0]; sampler_desc.BorderColor[1]; sampler_desc.BorderColor[2]; sampler_desc.BorderColor[3]; sampler_desc.MinLOD; sampler_desc.MaxLOD; hr = device->CreateSamplerState( &sampler_desc, &font_sampler_state_); if (FAILED(hr)) { XEFATAL("D3D11: unable to create invalid sampler state"); return false; } return true; } D3D11ProfilerDisplay::~D3D11ProfilerDisplay() { XESAFERELEASE(blend_state_); XESAFERELEASE(depth_stencil_state_); XESAFERELEASE(vertex_shader_); XESAFERELEASE(pixel_shader_); XESAFERELEASE(shader_constants_); XESAFERELEASE(shader_layout_); XESAFERELEASE(font_texture_view_); XESAFERELEASE(font_sampler_state_); XESAFERELEASE(vertex_buffer_); } uint32_t D3D11ProfilerDisplay::width() const { return window_->width(); } uint32_t D3D11ProfilerDisplay::height() const { return window_->height(); } void D3D11ProfilerDisplay::Begin() { auto context = window_->context(); D3D11_VIEWPORT viewport; viewport.TopLeftX = 0.0f; viewport.TopLeftY = 0.0f; viewport.Width = static_cast(width()); viewport.Height = static_cast(height()); viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; context->RSSetViewports(1, &viewport); // Setup projection matrix. float left = viewport.TopLeftX; float right = viewport.TopLeftX + viewport.Width; float bottom = viewport.TopLeftY + viewport.Height; float top = viewport.TopLeftY; float z_near = viewport.MinDepth; float z_far = viewport.MaxDepth; float projection[16] = { 0 }; projection[0] = 2.0f / (right - left); projection[5] = 2.0f / (top - bottom); projection[10] = -2.0f / (z_far - z_near); projection[12] = -(right + left) / (right - left); projection[13] = -(top + bottom) / (top - bottom); projection[14] = -(z_far + z_near) / (z_far - z_near); projection[15] = 1.0f; D3D11_MAPPED_SUBRESOURCE res; context->Map(shader_constants_, 0, D3D11_MAP_WRITE_DISCARD, 0, &res); memcpy(res.pData, projection, sizeof(projection)); context->Unmap(shader_constants_, 0); // Setup state. context->OMSetBlendState(blend_state_, { 0 }, 0xFFFFFFFF); context->OMSetDepthStencilState(depth_stencil_state_, 0); // Bind shaders. context->GSSetShader(nullptr, nullptr, 0); context->VSSetShader(vertex_shader_, nullptr, 0); context->VSSetConstantBuffers(0, 1, &shader_constants_); context->PSSetShader(pixel_shader_, nullptr, 0); context->PSSetConstantBuffers(0, 1, &shader_constants_); ID3D11SamplerState* ps_samplers[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT] = { font_sampler_state_, nullptr, }; context->PSSetSamplers(0, XECOUNT(ps_samplers), ps_samplers); ID3D11ShaderResourceView* ps_resources[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] = { font_texture_view_, nullptr, }; context->PSSetShaderResources(0, XECOUNT(ps_resources), ps_resources); context->IASetInputLayout(shader_layout_); } void D3D11ProfilerDisplay::End() { Flush(); } D3D11ProfilerDisplay::Vertex* D3D11ProfilerDisplay::AllocateVertices( D3D_PRIMITIVE_TOPOLOGY primitive, size_t count) { if (draw_state_.vertex_index + count > XECOUNT(draw_state_.vertex_buffer)) { Flush(); } assert_true(draw_state_.vertex_index + count <= XECOUNT(draw_state_.vertex_buffer)); size_t head = draw_state_.vertex_index; draw_state_.vertex_index += count; if (draw_state_.command_index && draw_state_.commands[draw_state_.command_index - 1].primitive == primitive) { draw_state_.commands[draw_state_.command_index - 1].vertex_count += count; } else { assert_true(draw_state_.command_index < XECOUNT(draw_state_.commands)); draw_state_.commands[draw_state_.command_index].primitive = primitive; draw_state_.commands[draw_state_.command_index].vertex_count = count; ++draw_state_.command_index; } return &draw_state_.vertex_buffer[head]; } void D3D11ProfilerDisplay::Flush() { auto context = window_->context(); if (!draw_state_.vertex_index) { return; } D3D11_MAPPED_SUBRESOURCE res; context->Map(vertex_buffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &res); memcpy(res.pData, draw_state_.vertex_buffer, sizeof(Vertex) * draw_state_.vertex_index); context->Unmap(vertex_buffer_, 0); uint32_t stride = 20; uint32_t offset = 0; context->IASetVertexBuffers(0, 1, &vertex_buffer_, &stride, &offset); size_t vertex_index = 0; for (int i = 0; i < draw_state_.command_index; ++i) { size_t count = draw_state_.commands[i].vertex_count; context->IASetPrimitiveTopology(draw_state_.commands[i].primitive); context->Draw((UINT)count, (UINT)vertex_index); vertex_index += count; } draw_state_.vertex_index = 0; draw_state_.command_index = 0; } #define Q0(d, member, v) d[0].member = v #define Q1(d, member, v) d[1].member = v; d[3].member = v #define Q2(d, member, v) d[4].member = v #define Q3(d, member, v) d[2].member = v; d[5].member = v void D3D11ProfilerDisplay::DrawBox( int x, int y, int x1, int y1, uint32_t color, BoxType type) { Vertex* v = AllocateVertices(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, 6); uint32_t color0; uint32_t color1; if (type == BOX_TYPE_FLAT) { color0 = 0xFF000000 | ((color & 0xFF) << 16) | (color & 0xFF00FF00) | ((color >> 16) & 0xFF); color1 = color0; } else { uint32_t r = 0xFF & (color >> 16); uint32_t g = 0xFF & (color >> 8); uint32_t b = 0xFF & color; uint32_t max_c = MAX(MAX(MAX(r, g), b), 30u); uint32_t min_c = MIN(MIN(MIN(r, g), b), 180u); uint32_t r0 = 0xFF & ((r + max_c)/2); uint32_t g0 = 0xFF & ((g + max_c)/2); uint32_t b0 = 0xFF & ((b + max_c)/2); uint32_t r1 = 0xFF & ((r + min_c) / 2); uint32_t g1 = 0xFF & ((g + min_c) / 2); uint32_t b1 = 0xFF & ((b + min_c) / 2); color0 = r0 | (g0 << 8) | (b0 << 16) | (0xFF000000 & color); color1 = r1 | (g1 << 8) | (b1 << 16) | (0xFF000000 & color); } Q0(v, x, (float)x); Q0(v, y, (float)y); Q0(v, color, color0); Q0(v, u, 2.0f); Q0(v, v, 2.0f); Q1(v, x, (float)x1); Q1(v, y, (float)y); Q1(v, color, color0); Q1(v, u, 3.0f); Q1(v, v, 2.0f); Q2(v, x, (float)x1); Q2(v, y, (float)y1); Q2(v, color, color1); Q2(v, u, 3.0f); Q2(v, v, 3.0f); Q3(v, x, (float)x); Q3(v, y, (float)y1); Q3(v, color, color1); Q3(v, u, 2.0f); Q3(v, v, 3.0f); } void D3D11ProfilerDisplay::DrawLine2D( uint32_t count, float* vertices, uint32_t color) { if (!count || !vertices) { return; } color = 0xFF000000 | ((color & 0xFF) << 16) | (color & 0xFF00FF00) | ((color >> 16) & 0xFF); Vertex* v = AllocateVertices(D3D11_PRIMITIVE_TOPOLOGY_LINELIST, 2 * (count - 1)); for (uint32_t i = 0; i < count - 1; ++i) { v[0].x = vertices[i * 2]; v[0].y = vertices[i * 2 + 1]; v[0].color = color; v[0].u = 2.0f; v[0].v = 2.0f; v[1].x = vertices[(i + 1) * 2]; v[1].y = vertices[(i + 1) * 2 + 1] ; v[1].color = color; v[1].u = 2.0f; v[1].v = 2.0f; v += 2; } } void D3D11ProfilerDisplay::DrawText( int x, int y, uint32_t color, const char* text, size_t text_length) { const float offset_u = 5.0f / 1024.0f; float fx = (float)x; float fy = (float)y; float fy2 = fy + (MICROPROFILE_TEXT_HEIGHT + 1); color = 0xFF000000 | ((color & 0xFF) << 16) | (color & 0xFF00FF00) | ((color >> 16) & 0xFF); Vertex* v = AllocateVertices(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, 6 * text_length); const char* s = text; for (uint32_t j = 0; j < text_length; ++j) { int16_t nOffset = font_description_.char_offsets[(int)*s++]; float fOffset = nOffset / 1024.f; Q0(v, x, fx); Q0(v, y, fy); Q0(v, color, color); Q0(v, u, fOffset); Q0(v, v, 0.0f); Q1(v, x, fx + MICROPROFILE_TEXT_WIDTH); Q1(v, y, fy); Q1(v, color, color); Q1(v, u, fOffset + offset_u); Q1(v, v, 0.0f); Q2(v, x, fx + MICROPROFILE_TEXT_WIDTH); Q2(v, y, fy2); Q2(v, color, color); Q2(v, u, fOffset + offset_u); Q2(v, v, 1.0f); Q3(v, x, fx); Q3(v, y, fy2); Q3(v, color, color); Q3(v, u, fOffset); Q3(v, v, 1.0f); fx += MICROPROFILE_TEXT_WIDTH + 1; v += 6; } }