diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index db11ef2ab..b77664482 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -193,6 +193,8 @@ uint64_t Processor::Execute( uint64_t Processor::ExecuteInterrupt( uint32_t cpu, uint64_t address, uint64_t arg0, uint64_t arg1) { + SCOPE_profile_cpu_f("cpu"); + // Acquire lock on interrupt thread (we can only dispatch one at a time). xe_mutex_lock(interrupt_thread_lock_); diff --git a/src/xenia/gpu/d3d11/d3d11_graphics_system.cc b/src/xenia/gpu/d3d11/d3d11_graphics_system.cc index f1432dc50..03c91038c 100644 --- a/src/xenia/gpu/d3d11/d3d11_graphics_system.cc +++ b/src/xenia/gpu/d3d11/d3d11_graphics_system.cc @@ -24,6 +24,12 @@ namespace { void __stdcall D3D11GraphicsSystemVsyncCallback( D3D11GraphicsSystem* gs, BOOLEAN) { + static bool thread_name_set = false; + if (!thread_name_set) { + thread_name_set = true; + Profiler::ThreadEnter("VsyncTimer"); + } + gs->MarkVblank(); gs->DispatchInterruptCallback(0); } @@ -53,7 +59,7 @@ void D3D11GraphicsSystem::Initialize() { (WAITORTIMERCALLBACK)D3D11GraphicsSystemVsyncCallback, this, 16, - 100, + 16, WT_EXECUTEINTIMERTHREAD); // Create DXGI factory so we can get a swap chain/etc. diff --git a/src/xenia/gpu/d3d11/d3d11_profiler_display.cc b/src/xenia/gpu/d3d11/d3d11_profiler_display.cc new file mode 100644 index 000000000..b0632b21f --- /dev/null +++ b/src/xenia/gpu/d3d11/d3d11_profiler_display.cc @@ -0,0 +1,630 @@ +/** + ****************************************************************************** + * 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. + XEASSERTALWAYS(); + } + + // 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_); + XEASSERT(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_); + XEASSERT(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(); + + // Setup projection matrix. + float left = 0.0f; + float right = (float)width(); + float bottom = (float)height(); + float top = 0.0f; + float z_near = -1.0f; + float z_far = 1.0f; + 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->PSSetSamplers(0, 1, &font_sampler_state_); + context->PSSetConstantBuffers(0, 1, &shader_constants_); + context->PSSetShaderResources(0, 1, &font_texture_view_); + 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(); + } + XEASSERT(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 { + XEASSERT(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; + } +} diff --git a/src/xenia/gpu/d3d11/d3d11_profiler_display.h b/src/xenia/gpu/d3d11/d3d11_profiler_display.h new file mode 100644 index 000000000..fd9f970f9 --- /dev/null +++ b/src/xenia/gpu/d3d11/d3d11_profiler_display.h @@ -0,0 +1,86 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_D3D11_D3D11_PROFILER_DISPLAY_H_ +#define XENIA_GPU_D3D11_D3D11_PROFILER_DISPLAY_H_ + +#include + +#include + + +namespace xe { +namespace gpu { +namespace d3d11 { + +class D3D11Window; + + +class D3D11ProfilerDisplay : public ProfilerDisplay { +public: + D3D11ProfilerDisplay(D3D11Window* window); + virtual ~D3D11ProfilerDisplay(); + + uint32_t width() const override; + uint32_t height() const override; + + // TODO(benvanik): GPU timestamping. + + void Begin() override; + void End() override; + void DrawBox(int x, int y, int x1, int y1, uint32_t color, BoxType type) override; + void DrawLine2D(uint32_t count, float* vertices, uint32_t color) override; + void DrawText(int x, int y, uint32_t color, const char* text, size_t text_length) override; + +private: + bool SetupState(); + bool SetupShaders(); + bool SetupFont(); + + struct Vertex { + float x, y; + float u, v; + uint32_t color; + }; + struct { + size_t vertex_index; + Vertex vertex_buffer[16 << 10]; + struct { + D3D11_PRIMITIVE_TOPOLOGY primitive; + size_t vertex_count; + } commands[32]; + size_t command_index; + } draw_state_; + Vertex* AllocateVertices(D3D_PRIMITIVE_TOPOLOGY primitive, size_t count); + void Flush(); + + D3D11Window* window_; + ID3D11BlendState* blend_state_; + ID3D11DepthStencilState* depth_stencil_state_; + ID3D11VertexShader* vertex_shader_; + ID3D11PixelShader* pixel_shader_; + ID3D11Buffer* shader_constants_; + ID3D11InputLayout* shader_layout_; + ID3D11ShaderResourceView* font_texture_view_; + ID3D11SamplerState* font_sampler_state_; + ID3D11Buffer* vertex_buffer_; + + struct { + uint16_t char_offsets[256]; + } font_description_; +}; + + + +} // namespace d3d11 +} // namespace gpu +} // namespace xe + + +#endif // XENIA_GPU_D3D11_D3D11_PROFILER_DISPLAY_H_ diff --git a/src/xenia/gpu/d3d11/d3d11_window.cc b/src/xenia/gpu/d3d11/d3d11_window.cc index 930149694..64a9b0df6 100644 --- a/src/xenia/gpu/d3d11/d3d11_window.cc +++ b/src/xenia/gpu/d3d11/d3d11_window.cc @@ -9,6 +9,8 @@ #include +#include + using namespace xe; using namespace xe::gpu; @@ -36,6 +38,7 @@ D3D11Window::D3D11Window( } D3D11Window::~D3D11Window() { + Profiler::set_display(nullptr); if (context_) { context_->ClearState(); } @@ -100,10 +103,21 @@ int D3D11Window::Initialize(const char* title, uint32_t width, uint32_t height) } context_->OMSetRenderTargets(1, &render_target_view_, NULL); + // Setup profiler display. + if (Profiler::is_enabled()) { + std::unique_ptr profiler_display( + new D3D11ProfilerDisplay(this)); + Profiler::set_display(std::move(profiler_display)); + } + return 0; } void D3D11Window::Swap() { + // Present profiler. + context_->OMSetRenderTargets(1, &render_target_view_, NULL); + Profiler::Present(); + // Swap buffers. // TODO(benvanik): control vsync with flag. bool vsync = true; diff --git a/src/xenia/gpu/d3d11/d3d11_window.h b/src/xenia/gpu/d3d11/d3d11_window.h index f9e47723d..df470df0f 100644 --- a/src/xenia/gpu/d3d11/d3d11_window.h +++ b/src/xenia/gpu/d3d11/d3d11_window.h @@ -29,7 +29,9 @@ public: IDXGIFactory1* dxgi_factory, ID3D11Device* device); virtual ~D3D11Window(); + ID3D11Device* device() const { return device_; } IDXGISwapChain* swap_chain() const { return swap_chain_; } + ID3D11DeviceContext* context() const { return context_; } virtual int Initialize(const char* title, uint32_t width, uint32_t height); diff --git a/src/xenia/gpu/d3d11/sources.gypi b/src/xenia/gpu/d3d11/sources.gypi index b1ad47ff4..6e0b193f5 100644 --- a/src/xenia/gpu/d3d11/sources.gypi +++ b/src/xenia/gpu/d3d11/sources.gypi @@ -10,6 +10,8 @@ 'd3d11_graphics_driver.h', 'd3d11_graphics_system.cc', 'd3d11_graphics_system.h', + 'd3d11_profiler_display.cc', + 'd3d11_profiler_display.h', 'd3d11_shader.cc', 'd3d11_shader.h', 'd3d11_shader_cache.cc', diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 8acce8b27..7a4c8f5d3 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -231,6 +231,10 @@ X_STATUS XThread::Create() { return return_code; } + char thread_name[32]; + xesnprintfa(thread_name, XECOUNT(thread_name), "XThread%04X", handle()); + set_name(thread_name); + module->Release(); return X_STATUS_SUCCESS; } diff --git a/src/xenia/kernel/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl_rtl.cc index 18310e6a7..9fb0bcdbd 100644 --- a/src/xenia/kernel/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl_rtl.cc @@ -633,9 +633,10 @@ spin: cs->recursion_count = 1; } - SHIM_CALL RtlEnterCriticalSection_shim( PPCContext* ppc_state, KernelState* state) { + SCOPE_profile_cpu_f("kernel"); + uint32_t cs_ptr = SHIM_GET_ARG_32(0); XELOGD("RtlEnterCriticalSection(%.8X)", cs_ptr); diff --git a/src/xenia/logging.cc b/src/xenia/logging.cc index 14b19aaf2..3f9d840f9 100644 --- a/src/xenia/logging.cc +++ b/src/xenia/logging.cc @@ -58,6 +58,8 @@ void xe_format_log_line( void xe_log_line(const char* file_path, const uint32_t line_number, const char* function_name, const char level_char, const char* fmt, ...) { + SCOPE_profile_cpu_i("emu", "log_line"); + char buffer[2048]; va_list args; va_start(args, fmt); diff --git a/src/xenia/profiling.cc b/src/xenia/profiling.cc index c7b5f2eb2..b72eba457 100644 --- a/src/xenia/profiling.cc +++ b/src/xenia/profiling.cc @@ -8,12 +8,18 @@ */ #define MICRO_PROFILE_IMPL +#define MICROPROFILE_USE_THREAD_NAME_CALLBACK 1 #include namespace xe { std::unique_ptr Profiler::display_ = nullptr; +void Profiler::Initialize() { + MicroProfileInit(); + MicroProfileSetDisplayMode(2); +} + void Profiler::Dump() { MicroProfileDumpTimers(); } @@ -23,6 +29,12 @@ void Profiler::Shutdown() { MicroProfileShutdown(); } +uint32_t Profiler::GetColor(const char* str) { + std::hash fn; + size_t value = fn(str); + return value & 0xFFFFFF; +} + void Profiler::ThreadEnter(const char* name) { MicroProfileOnThreadCreate(name); } @@ -31,6 +43,47 @@ void Profiler::ThreadExit() { MicroProfileOnThreadExit(); } +bool Profiler::OnKeyDown(int key_code) { + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx + switch (key_code) { + case VK_TAB: + MicroProfileToggleDisplayMode(); + return true; + case VK_OEM_3: // ` + MicroProfileTogglePause(); + return true; + case 0x31: // 1 + MicroProfileModKey(1); + return true; + } + return false; +} + +bool Profiler::OnKeyUp(int key_code) { + switch (key_code) { + case 0x31: // 1 + MicroProfileModKey(0); + return true; + } + return false; +} + +void Profiler::OnMouseDown(bool left_button, bool right_button) { + MicroProfileMouseButton(left_button, right_button); +} + +void Profiler::OnMouseUp() { + MicroProfileMouseButton(0, 0); +} + +void Profiler::OnMouseMove(int x, int y) { + MicroProfileMousePosition(x, y, 0); +} + +void Profiler::OnMouseWheel(int x, int y, int dy) { + MicroProfileMousePosition(x, y, dy); +} + void Profiler::set_display(std::unique_ptr display) { display_ = std::move(display); } @@ -60,6 +113,10 @@ uint64_t MicroProfileTicksPerSecondGpu() { return 0; } +const char* MicroProfileGetThreadName() { + return "TODO: get thread name!"; +} + void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor, MicroProfileBoxType type) { auto display = xe::Profiler::display(); if (!display) { diff --git a/src/xenia/profiling.h b/src/xenia/profiling.h index 8ab3d3169..bf66c68a5 100644 --- a/src/xenia/profiling.h +++ b/src/xenia/profiling.h @@ -29,16 +29,16 @@ namespace xe { // Defines a profiling scope for CPU tasks. // Use `SCOPE_profile_cpu(name)` to activate the scope. -#define DEFINE_profile_cpu(name, group_name, scope_name, color) \ - MICROPROFILE_DEFINE(name, group_name, scope_name, color) +#define DEFINE_profile_cpu(name, group_name, scope_name) \ + MICROPROFILE_DEFINE(name, group_name, scope_name, xe::Profiler::GetColor(scope_name)) // Declares a previously defined profile scope. Use in a translation unit. #define DECLARE_profile_cpu(name) MICROPROFILE_DECLARE(name) // Defines a profiling scope for GPU tasks. // Use `COUNT_profile_gpu(name)` to activate the scope. -#define DEFINE_profile_gpu(name, group_name, scope_name, color) \ - MICROPROFILE_DEFINE_GPU(name, group_name, scope_name, color) +#define DEFINE_profile_gpu(name, group_name, scope_name) \ + MICROPROFILE_DEFINE_GPU(name, group_name, scope_name, xe::Profiler::GetColor(scope_name)) // Declares a previously defined profile scope. Use in a translation unit. #define DECLARE_profile_gpu(name) MICROPROFILE_DECLARE_GPU(name) @@ -50,8 +50,13 @@ namespace xe { // Enters a CPU profiling scope, active for the duration of the containing // block. No previous definition required. -#define SCOPE_profile_cpu_i(group_name, scope_name, color) \ - MICROPROFILE_SCOPEI(group_name, scope_name, color) +#define SCOPE_profile_cpu_i(group_name, scope_name) \ + MICROPROFILE_SCOPEI(group_name, scope_name, xe::Profiler::GetColor(scope_name)) + +// Enters a CPU profiling scope by function name, active for the duration of +// the containing block. No previous definition required. +#define SCOPE_profile_cpu_f(group_name) \ + MICROPROFILE_SCOPEI(group_name, XE_CURRENT_FUNCTION, xe::Profiler::GetColor(XE_CURRENT_FUNCTION)) // Enters a previously defined GPU profiling scope, active for the duration // of the containing block. @@ -60,8 +65,13 @@ namespace xe { // Enters a GPU profiling scope, active for the duration of the containing // block. No previous definition required. -#define SCOPE_profile_gpu_i(group_name, scope_name, color) \ - MICROPROFILE_SCOPEGPUI(group_name, scope_name, color) +#define SCOPE_profile_gpu_i(group_name, scope_name) \ + MICROPROFILE_SCOPEGPUI(group_name, scope_name, xe::Profiler::GetColor(scope_name)) + +// Enters a GPU profiling scope by function name, active for the duration of +// the containing block. No previous definition required. +#define SCOPE_profile_gpu_f(group_name) \ + MICROPROFILE_SCOPEGPUI(group_name, XE_CURRENT_FUNCTION, xe::Profiler::GetColor(XE_CURRENT_FUNCTION)) // Tracks a CPU value counter. #define COUNT_profile_cpu(name, count) MICROPROFILE_META_CPU(name, count) @@ -105,17 +115,35 @@ public: class Profiler { public: +#if XE_OPTION_PROFILING + static bool is_enabled() { return true; } +#else + static bool is_enabled() { return false; } +#endif // XE_OPTION_PROFILING + + // Initializes the profiler. Call at startup. + static void Initialize(); // Dumps data to stdout. static void Dump(); // Cleans up profiling, releasing all memory. static void Shutdown(); + // Computes a color from the given string. + static uint32_t GetColor(const char* str); + // Activates the calling thread for profiling. // This must be called immediately after launching a thread. static void ThreadEnter(const char* name = nullptr); // Deactivates the calling thread for profiling. static void ThreadExit(); + static bool OnKeyDown(int key_code); + static bool OnKeyUp(int key_code); + static void OnMouseDown(bool left_button, bool right_button); + static void OnMouseUp(); + static void OnMouseMove(int x, int y); + static void OnMouseWheel(int x, int y, int dy); + // Gets the current display, if any. static ProfilerDisplay* display() { return display_.get(); } // Initializes drawing with the given display. @@ -124,7 +152,6 @@ public: static void Present(); // TODO(benvanik): display mode/pause/etc? - // TODO(benvanik): mouse, keys private: static std::unique_ptr display_; diff --git a/src/xenia/ui/ui_event.h b/src/xenia/ui/ui_event.h index 0c973b86f..07fae340e 100644 --- a/src/xenia/ui/ui_event.h +++ b/src/xenia/ui/ui_event.h @@ -32,6 +32,18 @@ private: Window* window_; }; +class KeyEvent : public UIEvent { +public: + KeyEvent(Window* window, int key_code) : + key_code_(key_code), + UIEvent(window) {} + virtual ~KeyEvent() {} + + int key_code() const { return key_code_; } + +private: + int key_code_; +}; class MouseEvent : public UIEvent { public: diff --git a/src/xenia/ui/win32/win32_window.cc b/src/xenia/ui/win32/win32_window.cc index 4bf5cc0cc..5e4b12738 100644 --- a/src/xenia/ui/win32/win32_window.cc +++ b/src/xenia/ui/win32/win32_window.cc @@ -281,12 +281,13 @@ bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) { } bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) { + auto e = KeyEvent(this, (int)wParam); switch (message) { case WM_KEYDOWN: - (byte)wParam; + key_down(e); return true; case WM_KEYUP: - (byte)wParam; + key_up(e); return true; default: return false; diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index 49fcbf17a..cdc7b2c39 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -48,6 +48,9 @@ public: alloy::Delegate closing; alloy::Delegate closed; + alloy::Delegate key_down; + alloy::Delegate key_up; + alloy::Delegate mouse_down; alloy::Delegate mouse_move; alloy::Delegate mouse_up; diff --git a/tools/alloy-sandbox/alloy-sandbox.cc b/tools/alloy-sandbox/alloy-sandbox.cc index e37a1d3d2..038580c69 100644 --- a/tools/alloy-sandbox/alloy-sandbox.cc +++ b/tools/alloy-sandbox/alloy-sandbox.cc @@ -24,6 +24,7 @@ using namespace xe::cpu; int alloy_sandbox(int argc, xechar_t** argv) { + Profiler::Initialize(); xe::Profiler::ThreadEnter("main"); XenonMemory* memory = new XenonMemory(); diff --git a/tools/xenia-run/xenia-run.cc b/tools/xenia-run/xenia-run.cc index 909e26dc8..9f2151d8c 100644 --- a/tools/xenia-run/xenia-run.cc +++ b/tools/xenia-run/xenia-run.cc @@ -22,6 +22,7 @@ DEFINE_string(target, "", int xenia_run(int argc, xechar_t** argv) { int result_code = 1; + Profiler::Initialize(); Profiler::ThreadEnter("main"); Emulator* emulator = NULL;