Microprofile integration.
This commit is contained in:
parent
cd56c30334
commit
beb9bd11f0
|
@ -193,6 +193,8 @@ uint64_t Processor::Execute(
|
||||||
|
|
||||||
uint64_t Processor::ExecuteInterrupt(
|
uint64_t Processor::ExecuteInterrupt(
|
||||||
uint32_t cpu, uint64_t address, uint64_t arg0, uint64_t arg1) {
|
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).
|
// Acquire lock on interrupt thread (we can only dispatch one at a time).
|
||||||
xe_mutex_lock(interrupt_thread_lock_);
|
xe_mutex_lock(interrupt_thread_lock_);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,12 @@ namespace {
|
||||||
|
|
||||||
void __stdcall D3D11GraphicsSystemVsyncCallback(
|
void __stdcall D3D11GraphicsSystemVsyncCallback(
|
||||||
D3D11GraphicsSystem* gs, BOOLEAN) {
|
D3D11GraphicsSystem* gs, BOOLEAN) {
|
||||||
|
static bool thread_name_set = false;
|
||||||
|
if (!thread_name_set) {
|
||||||
|
thread_name_set = true;
|
||||||
|
Profiler::ThreadEnter("VsyncTimer");
|
||||||
|
}
|
||||||
|
|
||||||
gs->MarkVblank();
|
gs->MarkVblank();
|
||||||
gs->DispatchInterruptCallback(0);
|
gs->DispatchInterruptCallback(0);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +59,7 @@ void D3D11GraphicsSystem::Initialize() {
|
||||||
(WAITORTIMERCALLBACK)D3D11GraphicsSystemVsyncCallback,
|
(WAITORTIMERCALLBACK)D3D11GraphicsSystemVsyncCallback,
|
||||||
this,
|
this,
|
||||||
16,
|
16,
|
||||||
100,
|
16,
|
||||||
WT_EXECUTEINTIMERTHREAD);
|
WT_EXECUTEINTIMERTHREAD);
|
||||||
|
|
||||||
// Create DXGI factory so we can get a swap chain/etc.
|
// Create DXGI factory so we can get a swap chain/etc.
|
||||||
|
|
|
@ -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 <xenia/gpu/d3d11/d3d11_profiler_display.h>
|
||||||
|
|
||||||
|
#include <xenia/gpu/gpu-private.h>
|
||||||
|
#include <xenia/gpu/d3d11/d3d11_window.h>
|
||||||
|
|
||||||
|
#include <d3dcompiler.h>
|
||||||
|
|
||||||
|
|
||||||
|
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<const char*>(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<const char*>(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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <xenia/core.h>
|
||||||
|
|
||||||
|
#include <d3d11.h>
|
||||||
|
|
||||||
|
|
||||||
|
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_
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include <xenia/gpu/d3d11/d3d11_window.h>
|
#include <xenia/gpu/d3d11/d3d11_window.h>
|
||||||
|
|
||||||
|
#include <xenia/gpu/d3d11/d3d11_profiler_display.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::gpu;
|
using namespace xe::gpu;
|
||||||
|
@ -36,6 +38,7 @@ D3D11Window::D3D11Window(
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D11Window::~D3D11Window() {
|
D3D11Window::~D3D11Window() {
|
||||||
|
Profiler::set_display(nullptr);
|
||||||
if (context_) {
|
if (context_) {
|
||||||
context_->ClearState();
|
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);
|
context_->OMSetRenderTargets(1, &render_target_view_, NULL);
|
||||||
|
|
||||||
|
// Setup profiler display.
|
||||||
|
if (Profiler::is_enabled()) {
|
||||||
|
std::unique_ptr<D3D11ProfilerDisplay> profiler_display(
|
||||||
|
new D3D11ProfilerDisplay(this));
|
||||||
|
Profiler::set_display(std::move(profiler_display));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11Window::Swap() {
|
void D3D11Window::Swap() {
|
||||||
|
// Present profiler.
|
||||||
|
context_->OMSetRenderTargets(1, &render_target_view_, NULL);
|
||||||
|
Profiler::Present();
|
||||||
|
|
||||||
// Swap buffers.
|
// Swap buffers.
|
||||||
// TODO(benvanik): control vsync with flag.
|
// TODO(benvanik): control vsync with flag.
|
||||||
bool vsync = true;
|
bool vsync = true;
|
||||||
|
|
|
@ -29,7 +29,9 @@ public:
|
||||||
IDXGIFactory1* dxgi_factory, ID3D11Device* device);
|
IDXGIFactory1* dxgi_factory, ID3D11Device* device);
|
||||||
virtual ~D3D11Window();
|
virtual ~D3D11Window();
|
||||||
|
|
||||||
|
ID3D11Device* device() const { return device_; }
|
||||||
IDXGISwapChain* swap_chain() const { return swap_chain_; }
|
IDXGISwapChain* swap_chain() const { return swap_chain_; }
|
||||||
|
ID3D11DeviceContext* context() const { return context_; }
|
||||||
|
|
||||||
virtual int Initialize(const char* title, uint32_t width, uint32_t height);
|
virtual int Initialize(const char* title, uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
'd3d11_graphics_driver.h',
|
'd3d11_graphics_driver.h',
|
||||||
'd3d11_graphics_system.cc',
|
'd3d11_graphics_system.cc',
|
||||||
'd3d11_graphics_system.h',
|
'd3d11_graphics_system.h',
|
||||||
|
'd3d11_profiler_display.cc',
|
||||||
|
'd3d11_profiler_display.h',
|
||||||
'd3d11_shader.cc',
|
'd3d11_shader.cc',
|
||||||
'd3d11_shader.h',
|
'd3d11_shader.h',
|
||||||
'd3d11_shader_cache.cc',
|
'd3d11_shader_cache.cc',
|
||||||
|
|
|
@ -231,6 +231,10 @@ X_STATUS XThread::Create() {
|
||||||
return return_code;
|
return return_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char thread_name[32];
|
||||||
|
xesnprintfa(thread_name, XECOUNT(thread_name), "XThread%04X", handle());
|
||||||
|
set_name(thread_name);
|
||||||
|
|
||||||
module->Release();
|
module->Release();
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -633,9 +633,10 @@ spin:
|
||||||
cs->recursion_count = 1;
|
cs->recursion_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SHIM_CALL RtlEnterCriticalSection_shim(
|
SHIM_CALL RtlEnterCriticalSection_shim(
|
||||||
PPCContext* ppc_state, KernelState* state) {
|
PPCContext* ppc_state, KernelState* state) {
|
||||||
|
SCOPE_profile_cpu_f("kernel");
|
||||||
|
|
||||||
uint32_t cs_ptr = SHIM_GET_ARG_32(0);
|
uint32_t cs_ptr = SHIM_GET_ARG_32(0);
|
||||||
|
|
||||||
XELOGD("RtlEnterCriticalSection(%.8X)", cs_ptr);
|
XELOGD("RtlEnterCriticalSection(%.8X)", cs_ptr);
|
||||||
|
|
|
@ -58,6 +58,8 @@ void xe_format_log_line(
|
||||||
void xe_log_line(const char* file_path, const uint32_t line_number,
|
void xe_log_line(const char* file_path, const uint32_t line_number,
|
||||||
const char* function_name, const char level_char,
|
const char* function_name, const char level_char,
|
||||||
const char* fmt, ...) {
|
const char* fmt, ...) {
|
||||||
|
SCOPE_profile_cpu_i("emu", "log_line");
|
||||||
|
|
||||||
char buffer[2048];
|
char buffer[2048];
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
|
|
@ -8,12 +8,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define MICRO_PROFILE_IMPL
|
#define MICRO_PROFILE_IMPL
|
||||||
|
#define MICROPROFILE_USE_THREAD_NAME_CALLBACK 1
|
||||||
#include <xenia/profiling.h>
|
#include <xenia/profiling.h>
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
std::unique_ptr<ProfilerDisplay> Profiler::display_ = nullptr;
|
std::unique_ptr<ProfilerDisplay> Profiler::display_ = nullptr;
|
||||||
|
|
||||||
|
void Profiler::Initialize() {
|
||||||
|
MicroProfileInit();
|
||||||
|
MicroProfileSetDisplayMode(2);
|
||||||
|
}
|
||||||
|
|
||||||
void Profiler::Dump() {
|
void Profiler::Dump() {
|
||||||
MicroProfileDumpTimers();
|
MicroProfileDumpTimers();
|
||||||
}
|
}
|
||||||
|
@ -23,6 +29,12 @@ void Profiler::Shutdown() {
|
||||||
MicroProfileShutdown();
|
MicroProfileShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Profiler::GetColor(const char* str) {
|
||||||
|
std::hash<std::string> fn;
|
||||||
|
size_t value = fn(str);
|
||||||
|
return value & 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
void Profiler::ThreadEnter(const char* name) {
|
void Profiler::ThreadEnter(const char* name) {
|
||||||
MicroProfileOnThreadCreate(name);
|
MicroProfileOnThreadCreate(name);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +43,47 @@ void Profiler::ThreadExit() {
|
||||||
MicroProfileOnThreadExit();
|
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<ProfilerDisplay> display) {
|
void Profiler::set_display(std::unique_ptr<ProfilerDisplay> display) {
|
||||||
display_ = std::move(display);
|
display_ = std::move(display);
|
||||||
}
|
}
|
||||||
|
@ -60,6 +113,10 @@ uint64_t MicroProfileTicksPerSecondGpu() {
|
||||||
return 0;
|
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) {
|
void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor, MicroProfileBoxType type) {
|
||||||
auto display = xe::Profiler::display();
|
auto display = xe::Profiler::display();
|
||||||
if (!display) {
|
if (!display) {
|
||||||
|
|
|
@ -29,16 +29,16 @@ namespace xe {
|
||||||
|
|
||||||
// Defines a profiling scope for CPU tasks.
|
// Defines a profiling scope for CPU tasks.
|
||||||
// Use `SCOPE_profile_cpu(name)` to activate the scope.
|
// Use `SCOPE_profile_cpu(name)` to activate the scope.
|
||||||
#define DEFINE_profile_cpu(name, group_name, scope_name, color) \
|
#define DEFINE_profile_cpu(name, group_name, scope_name) \
|
||||||
MICROPROFILE_DEFINE(name, group_name, scope_name, color)
|
MICROPROFILE_DEFINE(name, group_name, scope_name, xe::Profiler::GetColor(scope_name))
|
||||||
|
|
||||||
// Declares a previously defined profile scope. Use in a translation unit.
|
// Declares a previously defined profile scope. Use in a translation unit.
|
||||||
#define DECLARE_profile_cpu(name) MICROPROFILE_DECLARE(name)
|
#define DECLARE_profile_cpu(name) MICROPROFILE_DECLARE(name)
|
||||||
|
|
||||||
// Defines a profiling scope for GPU tasks.
|
// Defines a profiling scope for GPU tasks.
|
||||||
// Use `COUNT_profile_gpu(name)` to activate the scope.
|
// Use `COUNT_profile_gpu(name)` to activate the scope.
|
||||||
#define DEFINE_profile_gpu(name, group_name, scope_name, color) \
|
#define DEFINE_profile_gpu(name, group_name, scope_name) \
|
||||||
MICROPROFILE_DEFINE_GPU(name, group_name, scope_name, color)
|
MICROPROFILE_DEFINE_GPU(name, group_name, scope_name, xe::Profiler::GetColor(scope_name))
|
||||||
|
|
||||||
// Declares a previously defined profile scope. Use in a translation unit.
|
// Declares a previously defined profile scope. Use in a translation unit.
|
||||||
#define DECLARE_profile_gpu(name) MICROPROFILE_DECLARE_GPU(name)
|
#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
|
// Enters a CPU profiling scope, active for the duration of the containing
|
||||||
// block. No previous definition required.
|
// block. No previous definition required.
|
||||||
#define SCOPE_profile_cpu_i(group_name, scope_name, color) \
|
#define SCOPE_profile_cpu_i(group_name, scope_name) \
|
||||||
MICROPROFILE_SCOPEI(group_name, scope_name, color)
|
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
|
// Enters a previously defined GPU profiling scope, active for the duration
|
||||||
// of the containing block.
|
// of the containing block.
|
||||||
|
@ -60,8 +65,13 @@ namespace xe {
|
||||||
|
|
||||||
// Enters a GPU profiling scope, active for the duration of the containing
|
// Enters a GPU profiling scope, active for the duration of the containing
|
||||||
// block. No previous definition required.
|
// block. No previous definition required.
|
||||||
#define SCOPE_profile_gpu_i(group_name, scope_name, color) \
|
#define SCOPE_profile_gpu_i(group_name, scope_name) \
|
||||||
MICROPROFILE_SCOPEGPUI(group_name, scope_name, color)
|
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.
|
// Tracks a CPU value counter.
|
||||||
#define COUNT_profile_cpu(name, count) MICROPROFILE_META_CPU(name, count)
|
#define COUNT_profile_cpu(name, count) MICROPROFILE_META_CPU(name, count)
|
||||||
|
@ -105,17 +115,35 @@ public:
|
||||||
|
|
||||||
class Profiler {
|
class Profiler {
|
||||||
public:
|
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.
|
// Dumps data to stdout.
|
||||||
static void Dump();
|
static void Dump();
|
||||||
// Cleans up profiling, releasing all memory.
|
// Cleans up profiling, releasing all memory.
|
||||||
static void Shutdown();
|
static void Shutdown();
|
||||||
|
|
||||||
|
// Computes a color from the given string.
|
||||||
|
static uint32_t GetColor(const char* str);
|
||||||
|
|
||||||
// Activates the calling thread for profiling.
|
// Activates the calling thread for profiling.
|
||||||
// This must be called immediately after launching a thread.
|
// This must be called immediately after launching a thread.
|
||||||
static void ThreadEnter(const char* name = nullptr);
|
static void ThreadEnter(const char* name = nullptr);
|
||||||
// Deactivates the calling thread for profiling.
|
// Deactivates the calling thread for profiling.
|
||||||
static void ThreadExit();
|
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.
|
// Gets the current display, if any.
|
||||||
static ProfilerDisplay* display() { return display_.get(); }
|
static ProfilerDisplay* display() { return display_.get(); }
|
||||||
// Initializes drawing with the given display.
|
// Initializes drawing with the given display.
|
||||||
|
@ -124,7 +152,6 @@ public:
|
||||||
static void Present();
|
static void Present();
|
||||||
|
|
||||||
// TODO(benvanik): display mode/pause/etc?
|
// TODO(benvanik): display mode/pause/etc?
|
||||||
// TODO(benvanik): mouse, keys
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::unique_ptr<ProfilerDisplay> display_;
|
static std::unique_ptr<ProfilerDisplay> display_;
|
||||||
|
|
|
@ -32,6 +32,18 @@ private:
|
||||||
Window* window_;
|
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 {
|
class MouseEvent : public UIEvent {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -281,12 +281,13 @@ bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) {
|
bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||||
|
auto e = KeyEvent(this, (int)wParam);
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
(byte)wParam;
|
key_down(e);
|
||||||
return true;
|
return true;
|
||||||
case WM_KEYUP:
|
case WM_KEYUP:
|
||||||
(byte)wParam;
|
key_up(e);
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -48,6 +48,9 @@ public:
|
||||||
alloy::Delegate<UIEvent> closing;
|
alloy::Delegate<UIEvent> closing;
|
||||||
alloy::Delegate<UIEvent> closed;
|
alloy::Delegate<UIEvent> closed;
|
||||||
|
|
||||||
|
alloy::Delegate<KeyEvent> key_down;
|
||||||
|
alloy::Delegate<KeyEvent> key_up;
|
||||||
|
|
||||||
alloy::Delegate<MouseEvent> mouse_down;
|
alloy::Delegate<MouseEvent> mouse_down;
|
||||||
alloy::Delegate<MouseEvent> mouse_move;
|
alloy::Delegate<MouseEvent> mouse_move;
|
||||||
alloy::Delegate<MouseEvent> mouse_up;
|
alloy::Delegate<MouseEvent> mouse_up;
|
||||||
|
|
|
@ -24,6 +24,7 @@ using namespace xe::cpu;
|
||||||
|
|
||||||
|
|
||||||
int alloy_sandbox(int argc, xechar_t** argv) {
|
int alloy_sandbox(int argc, xechar_t** argv) {
|
||||||
|
Profiler::Initialize();
|
||||||
xe::Profiler::ThreadEnter("main");
|
xe::Profiler::ThreadEnter("main");
|
||||||
|
|
||||||
XenonMemory* memory = new XenonMemory();
|
XenonMemory* memory = new XenonMemory();
|
||||||
|
|
|
@ -22,6 +22,7 @@ DEFINE_string(target, "",
|
||||||
int xenia_run(int argc, xechar_t** argv) {
|
int xenia_run(int argc, xechar_t** argv) {
|
||||||
int result_code = 1;
|
int result_code = 1;
|
||||||
|
|
||||||
|
Profiler::Initialize();
|
||||||
Profiler::ThreadEnter("main");
|
Profiler::ThreadEnter("main");
|
||||||
|
|
||||||
Emulator* emulator = NULL;
|
Emulator* emulator = NULL;
|
||||||
|
|
Loading…
Reference in New Issue