Microprofile integration.

This commit is contained in:
Ben Vanik 2014-05-28 13:59:43 -07:00
parent cd56c30334
commit beb9bd11f0
17 changed files with 864 additions and 13 deletions

View File

@ -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_);

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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_

View File

@ -9,6 +9,8 @@
#include <xenia/gpu/d3d11/d3d11_window.h>
#include <xenia/gpu/d3d11/d3d11_profiler_display.h>
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<D3D11ProfilerDisplay> 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;

View File

@ -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);

View File

@ -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',

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -8,12 +8,18 @@
*/
#define MICRO_PROFILE_IMPL
#define MICROPROFILE_USE_THREAD_NAME_CALLBACK 1
#include <xenia/profiling.h>
namespace xe {
std::unique_ptr<ProfilerDisplay> 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<std::string> 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<ProfilerDisplay> 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) {

View File

@ -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<ProfilerDisplay> display_;

View File

@ -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:

View File

@ -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;

View File

@ -48,6 +48,9 @@ public:
alloy::Delegate<UIEvent> closing;
alloy::Delegate<UIEvent> closed;
alloy::Delegate<KeyEvent> key_down;
alloy::Delegate<KeyEvent> key_up;
alloy::Delegate<MouseEvent> mouse_down;
alloy::Delegate<MouseEvent> mouse_move;
alloy::Delegate<MouseEvent> mouse_up;

View File

@ -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();

View File

@ -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;