commit
21e0a293fc
|
@ -48,13 +48,86 @@ bool DrawBatcher::Initialize(CircularBuffer* array_data_buffer) {
|
||||||
if (!state_buffer_.Initialize()) {
|
if (!state_buffer_.Initialize()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!InitializeTFB()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command_buffer_.handle());
|
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command_buffer_.handle());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initializes a transform feedback object
|
||||||
|
// We use this to capture vertex data straight from the vertex/geometry shader.
|
||||||
|
bool DrawBatcher::InitializeTFB() {
|
||||||
|
glCreateBuffers(1, &tfvbo_);
|
||||||
|
if (!tfvbo_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glCreateTransformFeedbacks(1, &tfbo_);
|
||||||
|
if (!tfbo_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glCreateQueries(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 1, &tfqo_);
|
||||||
|
if (!tfqo_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(DrChat): Calculate this based on the number of primitives drawn.
|
||||||
|
glNamedBufferData(tfvbo_, 16384 * 4, nullptr, GL_STATIC_READ);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawBatcher::ShutdownTFB() {
|
||||||
|
glDeleteBuffers(1, &tfvbo_);
|
||||||
|
glDeleteTransformFeedbacks(1, &tfbo_);
|
||||||
|
glDeleteQueries(1, &tfqo_);
|
||||||
|
|
||||||
|
tfvbo_ = 0;
|
||||||
|
tfbo_ = 0;
|
||||||
|
tfqo_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DrawBatcher::QueryTFBSize() {
|
||||||
|
if (!tfb_enabled_) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = 0;
|
||||||
|
switch (tfb_prim_type_gl_) {
|
||||||
|
case GL_POINTS:
|
||||||
|
size = tfb_prim_count_ * 1 * 4 * 4;
|
||||||
|
break;
|
||||||
|
case GL_LINES:
|
||||||
|
size = tfb_prim_count_ * 2 * 4 * 4;
|
||||||
|
break;
|
||||||
|
case GL_TRIANGLES:
|
||||||
|
size = tfb_prim_count_ * 3 * 4 * 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrawBatcher::ReadbackTFB(void* buffer, size_t size) {
|
||||||
|
if (!tfb_enabled_) {
|
||||||
|
XELOGW("DrawBatcher::ReadbackTFB called when TFB was disabled!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* data = glMapNamedBufferRange(tfvbo_, 0, size, GL_MAP_READ_BIT);
|
||||||
|
std::memcpy(buffer, data, size);
|
||||||
|
glUnmapNamedBuffer(tfvbo_);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DrawBatcher::Shutdown() {
|
void DrawBatcher::Shutdown() {
|
||||||
command_buffer_.Shutdown();
|
command_buffer_.Shutdown();
|
||||||
state_buffer_.Shutdown();
|
state_buffer_.Shutdown();
|
||||||
|
ShutdownTFB();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrawBatcher::ReconfigurePipeline(GL4Shader* vertex_shader,
|
bool DrawBatcher::ReconfigurePipeline(GL4Shader* vertex_shader,
|
||||||
|
@ -242,6 +315,84 @@ bool DrawBatcher::CommitDraw() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrawBatcher::TFBBegin(PrimitiveType prim_type) {
|
||||||
|
if (!tfb_enabled_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate the primitive typename to something compatible with TFB.
|
||||||
|
GLenum gl_prim_type = 0;
|
||||||
|
switch (prim_type) {
|
||||||
|
case PrimitiveType::kLineList:
|
||||||
|
gl_prim_type = GL_LINES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kLineStrip:
|
||||||
|
gl_prim_type = GL_LINES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kLineLoop:
|
||||||
|
gl_prim_type = GL_LINES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kPointList:
|
||||||
|
// The geometry shader associated with this writes out triangles.
|
||||||
|
gl_prim_type = GL_TRIANGLES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kTriangleList:
|
||||||
|
gl_prim_type = GL_TRIANGLES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kTriangleStrip:
|
||||||
|
gl_prim_type = GL_TRIANGLES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kRectangleList:
|
||||||
|
gl_prim_type = GL_TRIANGLES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kTriangleFan:
|
||||||
|
gl_prim_type = GL_TRIANGLES;
|
||||||
|
break;
|
||||||
|
case PrimitiveType::kQuadList:
|
||||||
|
// FIXME: In some cases the geometry shader will output lines.
|
||||||
|
// See: GL4CommandProcessor::UpdateShaders
|
||||||
|
gl_prim_type = GL_TRIANGLES;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert_unhandled_case(prim_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(DrChat): Resize the TFVBO here.
|
||||||
|
// Could draw a 2nd time with the rasterizer disabled once we have a primitive
|
||||||
|
// count.
|
||||||
|
|
||||||
|
tfb_prim_type_ = prim_type;
|
||||||
|
tfb_prim_type_gl_ = gl_prim_type;
|
||||||
|
|
||||||
|
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfbo_);
|
||||||
|
|
||||||
|
// Bind the buffer to the TFB object.
|
||||||
|
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfvbo_);
|
||||||
|
|
||||||
|
// Begin a query for # prims written
|
||||||
|
glBeginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0, tfqo_);
|
||||||
|
|
||||||
|
// Begin capturing.
|
||||||
|
glBeginTransformFeedback(gl_prim_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawBatcher::TFBEnd() {
|
||||||
|
if (!tfb_enabled_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glEndTransformFeedback();
|
||||||
|
glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0);
|
||||||
|
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
|
||||||
|
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
|
||||||
|
|
||||||
|
// Cache the query size as query objects aren't shared.
|
||||||
|
GLint prim_count = 0;
|
||||||
|
glGetQueryObjectiv(tfqo_, GL_QUERY_RESULT, &prim_count);
|
||||||
|
tfb_prim_count_ = prim_count;
|
||||||
|
}
|
||||||
|
|
||||||
bool DrawBatcher::Flush(FlushMode mode) {
|
bool DrawBatcher::Flush(FlushMode mode) {
|
||||||
GLboolean cull_enabled = 0;
|
GLboolean cull_enabled = 0;
|
||||||
if (batch_state_.draw_count) {
|
if (batch_state_.draw_count) {
|
||||||
|
@ -263,6 +414,7 @@ bool DrawBatcher::Flush(FlushMode mode) {
|
||||||
batch_state_.state_range_length);
|
batch_state_.state_range_length);
|
||||||
|
|
||||||
GLenum prim_type = 0;
|
GLenum prim_type = 0;
|
||||||
|
bool valid_prim = true;
|
||||||
switch (batch_state_.prim_type) {
|
switch (batch_state_.prim_type) {
|
||||||
case PrimitiveType::kPointList:
|
case PrimitiveType::kPointList:
|
||||||
prim_type = GL_POINTS;
|
prim_type = GL_POINTS;
|
||||||
|
@ -291,8 +443,6 @@ bool DrawBatcher::Flush(FlushMode mode) {
|
||||||
// assert_true(
|
// assert_true(
|
||||||
// (register_file_->values[XE_GPU_REG_PA_SU_SC_MODE_CNTL].u32
|
// (register_file_->values[XE_GPU_REG_PA_SU_SC_MODE_CNTL].u32
|
||||||
// & 0x3) == 0);
|
// & 0x3) == 0);
|
||||||
cull_enabled = glIsEnabled(GL_CULL_FACE);
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
break;
|
break;
|
||||||
case PrimitiveType::kQuadList:
|
case PrimitiveType::kQuadList:
|
||||||
prim_type = GL_LINES_ADJACENCY;
|
prim_type = GL_LINES_ADJACENCY;
|
||||||
|
@ -300,17 +450,21 @@ bool DrawBatcher::Flush(FlushMode mode) {
|
||||||
default:
|
default:
|
||||||
case PrimitiveType::kUnknown0x07:
|
case PrimitiveType::kUnknown0x07:
|
||||||
prim_type = GL_POINTS;
|
prim_type = GL_POINTS;
|
||||||
|
valid_prim = false;
|
||||||
XELOGE("unsupported primitive type %d", batch_state_.prim_type);
|
XELOGE("unsupported primitive type %d", batch_state_.prim_type);
|
||||||
assert_unhandled_case(batch_state_.prim_type);
|
assert_unhandled_case(batch_state_.prim_type);
|
||||||
DiscardDraw();
|
break;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast path for single draws.
|
// Fast path for single draws.
|
||||||
void* indirect_offset =
|
void* indirect_offset =
|
||||||
reinterpret_cast<void*>(batch_state_.command_range_start);
|
reinterpret_cast<void*>(batch_state_.command_range_start);
|
||||||
|
|
||||||
if (batch_state_.draw_count == 1) {
|
if (tfb_enabled_) {
|
||||||
|
TFBBegin(batch_state_.prim_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid_prim && batch_state_.draw_count == 1) {
|
||||||
// Fast path for one draw. Removes MDI overhead when not required.
|
// Fast path for one draw. Removes MDI overhead when not required.
|
||||||
if (batch_state_.indexed) {
|
if (batch_state_.indexed) {
|
||||||
auto& cmd = active_draw_.draw_elements_cmd;
|
auto& cmd = active_draw_.draw_elements_cmd;
|
||||||
|
@ -326,7 +480,7 @@ bool DrawBatcher::Flush(FlushMode mode) {
|
||||||
cmd->count, cmd->instance_count,
|
cmd->count, cmd->instance_count,
|
||||||
cmd->base_instance);
|
cmd->base_instance);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (valid_prim) {
|
||||||
// Full multi-draw.
|
// Full multi-draw.
|
||||||
if (batch_state_.indexed) {
|
if (batch_state_.indexed) {
|
||||||
glMultiDrawElementsIndirect(prim_type, batch_state_.index_type,
|
glMultiDrawElementsIndirect(prim_type, batch_state_.index_type,
|
||||||
|
@ -339,6 +493,10 @@ bool DrawBatcher::Flush(FlushMode mode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tfb_enabled_) {
|
||||||
|
TFBEnd();
|
||||||
|
}
|
||||||
|
|
||||||
batch_state_.command_range_start = UINTPTR_MAX;
|
batch_state_.command_range_start = UINTPTR_MAX;
|
||||||
batch_state_.command_range_length = 0;
|
batch_state_.command_range_length = 0;
|
||||||
batch_state_.state_range_start = UINTPTR_MAX;
|
batch_state_.state_range_start = UINTPTR_MAX;
|
||||||
|
@ -346,10 +504,6 @@ bool DrawBatcher::Flush(FlushMode mode) {
|
||||||
batch_state_.draw_count = 0;
|
batch_state_.draw_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batch_state_.prim_type == PrimitiveType::kRectangleList && cull_enabled) {
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == FlushMode::kReconfigure) {
|
if (mode == FlushMode::kReconfigure) {
|
||||||
// Reset - we'll update it as soon as we have all the information.
|
// Reset - we'll update it as soon as we have all the information.
|
||||||
batch_state_.needs_reconfigure = true;
|
batch_state_.needs_reconfigure = true;
|
||||||
|
|
|
@ -99,7 +99,21 @@ class DrawBatcher {
|
||||||
bool CommitDraw();
|
bool CommitDraw();
|
||||||
bool Flush(FlushMode mode);
|
bool Flush(FlushMode mode);
|
||||||
|
|
||||||
|
// TFB - Filled with vertex shader output from the last flush.
|
||||||
|
size_t QueryTFBSize();
|
||||||
|
bool ReadbackTFB(void* buffer, size_t size);
|
||||||
|
|
||||||
|
GLuint tfvbo() { return tfvbo_; }
|
||||||
|
bool is_tfb_enabled() const { return tfb_enabled_; }
|
||||||
|
void set_tfb_enabled(bool enabled) { tfb_enabled_ = enabled; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool InitializeTFB();
|
||||||
|
void ShutdownTFB();
|
||||||
|
|
||||||
|
void TFBBegin(PrimitiveType prim_type);
|
||||||
|
void TFBEnd();
|
||||||
|
|
||||||
bool BeginDraw();
|
bool BeginDraw();
|
||||||
void CopyConstants();
|
void CopyConstants();
|
||||||
|
|
||||||
|
@ -108,6 +122,14 @@ class DrawBatcher {
|
||||||
CircularBuffer state_buffer_;
|
CircularBuffer state_buffer_;
|
||||||
CircularBuffer* array_data_buffer_;
|
CircularBuffer* array_data_buffer_;
|
||||||
|
|
||||||
|
GLuint tfbo_ = 0;
|
||||||
|
GLuint tfvbo_ = 0;
|
||||||
|
GLuint tfqo_ = 0;
|
||||||
|
PrimitiveType tfb_prim_type_ = PrimitiveType::kNone;
|
||||||
|
GLenum tfb_prim_type_gl_ = 0;
|
||||||
|
GLint tfb_prim_count_ = 0;
|
||||||
|
bool tfb_enabled_ = false;
|
||||||
|
|
||||||
struct BatchState {
|
struct BatchState {
|
||||||
bool needs_reconfigure;
|
bool needs_reconfigure;
|
||||||
PrimitiveType prim_type;
|
PrimitiveType prim_type;
|
||||||
|
|
|
@ -578,7 +578,7 @@ bool GL4CommandProcessor::IssueDraw(PrimitiveType prim_type,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = UpdateState();
|
status = UpdateState(draw_batcher_.prim_type());
|
||||||
CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render state");
|
CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render state");
|
||||||
status = PopulateSamplers();
|
status = PopulateSamplers();
|
||||||
CHECK_ISSUE_UPDATE_STATUS(status, mismatch,
|
CHECK_ISSUE_UPDATE_STATUS(status, mismatch,
|
||||||
|
@ -665,6 +665,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateShaders(
|
||||||
|
|
||||||
// Normal vertex shaders only, for now.
|
// Normal vertex shaders only, for now.
|
||||||
// TODO(benvanik): transform feedback/memexport.
|
// TODO(benvanik): transform feedback/memexport.
|
||||||
|
// https://github.com/freedreno/freedreno/blob/master/includes/a2xx.xml.h
|
||||||
// 0 = normal
|
// 0 = normal
|
||||||
// 2 = point size
|
// 2 = point size
|
||||||
assert_true(program_cntl.vs_export_mode == 0 ||
|
assert_true(program_cntl.vs_export_mode == 0 ||
|
||||||
|
@ -871,7 +872,8 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRenderTargets() {
|
||||||
return UpdateStatus::kMismatch;
|
return UpdateStatus::kMismatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateState() {
|
GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateState(
|
||||||
|
PrimitiveType prim_type) {
|
||||||
bool mismatch = false;
|
bool mismatch = false;
|
||||||
|
|
||||||
#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \
|
#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \
|
||||||
|
@ -887,7 +889,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateState() {
|
||||||
UpdateStatus status;
|
UpdateStatus status;
|
||||||
status = UpdateViewportState();
|
status = UpdateViewportState();
|
||||||
CHECK_UPDATE_STATUS(status, mismatch, "Unable to update viewport state");
|
CHECK_UPDATE_STATUS(status, mismatch, "Unable to update viewport state");
|
||||||
status = UpdateRasterizerState();
|
status = UpdateRasterizerState(prim_type);
|
||||||
CHECK_UPDATE_STATUS(status, mismatch, "Unable to update rasterizer state");
|
CHECK_UPDATE_STATUS(status, mismatch, "Unable to update rasterizer state");
|
||||||
status = UpdateBlendState();
|
status = UpdateBlendState();
|
||||||
CHECK_UPDATE_STATUS(status, mismatch, "Unable to update blend state");
|
CHECK_UPDATE_STATUS(status, mismatch, "Unable to update blend state");
|
||||||
|
@ -1055,7 +1057,8 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateViewportState() {
|
||||||
return UpdateStatus::kMismatch;
|
return UpdateStatus::kMismatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRasterizerState() {
|
GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRasterizerState(
|
||||||
|
PrimitiveType prim_type) {
|
||||||
auto& regs = update_rasterizer_state_regs_;
|
auto& regs = update_rasterizer_state_regs_;
|
||||||
|
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
|
@ -1067,10 +1070,13 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRasterizerState() {
|
||||||
XE_GPU_REG_PA_SC_SCREEN_SCISSOR_BR);
|
XE_GPU_REG_PA_SC_SCREEN_SCISSOR_BR);
|
||||||
dirty |= SetShadowRegister(®s.multi_prim_ib_reset_index,
|
dirty |= SetShadowRegister(®s.multi_prim_ib_reset_index,
|
||||||
XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX);
|
XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX);
|
||||||
|
dirty |= regs.prim_type != prim_type;
|
||||||
if (!dirty) {
|
if (!dirty) {
|
||||||
return UpdateStatus::kCompatible;
|
return UpdateStatus::kCompatible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regs.prim_type = prim_type;
|
||||||
|
|
||||||
SCOPE_profile_cpu_f("gpu");
|
SCOPE_profile_cpu_f("gpu");
|
||||||
|
|
||||||
draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange);
|
draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange);
|
||||||
|
@ -1113,6 +1119,11 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRasterizerState() {
|
||||||
glFrontFace(GL_CCW);
|
glFrontFace(GL_CCW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prim_type == PrimitiveType::kRectangleList) {
|
||||||
|
// Rectangle lists aren't culled. There may be other things they skip too.
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
}
|
||||||
|
|
||||||
static const GLenum kFillModes[3] = {
|
static const GLenum kFillModes[3] = {
|
||||||
GL_POINT, GL_LINE, GL_FILL,
|
GL_POINT, GL_LINE, GL_FILL,
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,6 +49,7 @@ class GL4CommandProcessor : public CommandProcessor {
|
||||||
|
|
||||||
// HACK: for debugging; would be good to have this in a base type.
|
// HACK: for debugging; would be good to have this in a base type.
|
||||||
TextureCache* texture_cache() { return &texture_cache_; }
|
TextureCache* texture_cache() { return &texture_cache_; }
|
||||||
|
DrawBatcher* draw_batcher() { return &draw_batcher_; }
|
||||||
|
|
||||||
GLuint GetColorRenderTarget(uint32_t pitch, MsaaSamples samples,
|
GLuint GetColorRenderTarget(uint32_t pitch, MsaaSamples samples,
|
||||||
uint32_t base, ColorRenderTargetFormat format);
|
uint32_t base, ColorRenderTargetFormat format);
|
||||||
|
@ -115,9 +116,9 @@ class GL4CommandProcessor : public CommandProcessor {
|
||||||
IndexBufferInfo* index_buffer_info) override;
|
IndexBufferInfo* index_buffer_info) override;
|
||||||
UpdateStatus UpdateShaders(PrimitiveType prim_type);
|
UpdateStatus UpdateShaders(PrimitiveType prim_type);
|
||||||
UpdateStatus UpdateRenderTargets();
|
UpdateStatus UpdateRenderTargets();
|
||||||
UpdateStatus UpdateState();
|
UpdateStatus UpdateState(PrimitiveType prim_type);
|
||||||
UpdateStatus UpdateViewportState();
|
UpdateStatus UpdateViewportState();
|
||||||
UpdateStatus UpdateRasterizerState();
|
UpdateStatus UpdateRasterizerState(PrimitiveType prim_type);
|
||||||
UpdateStatus UpdateBlendState();
|
UpdateStatus UpdateBlendState();
|
||||||
UpdateStatus UpdateDepthStencilState();
|
UpdateStatus UpdateDepthStencilState();
|
||||||
UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info);
|
UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info);
|
||||||
|
@ -191,6 +192,7 @@ class GL4CommandProcessor : public CommandProcessor {
|
||||||
uint32_t pa_sc_screen_scissor_tl;
|
uint32_t pa_sc_screen_scissor_tl;
|
||||||
uint32_t pa_sc_screen_scissor_br;
|
uint32_t pa_sc_screen_scissor_br;
|
||||||
uint32_t multi_prim_ib_reset_index;
|
uint32_t multi_prim_ib_reset_index;
|
||||||
|
PrimitiveType prim_type;
|
||||||
|
|
||||||
UpdateRasterizerStateRegisters() { Reset(); }
|
UpdateRasterizerStateRegisters() { Reset(); }
|
||||||
void Reset() { std::memset(this, 0, sizeof(*this)); }
|
void Reset() { std::memset(this, 0, sizeof(*this)); }
|
||||||
|
|
|
@ -56,6 +56,43 @@ class GL4TraceViewer : public TraceViewer {
|
||||||
auto texture = entry_view->texture;
|
auto texture = entry_view->texture;
|
||||||
return static_cast<uintptr_t>(texture->handle);
|
return static_cast<uintptr_t>(texture->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t QueryVSOutputSize() override {
|
||||||
|
auto command_processor = static_cast<GL4CommandProcessor*>(
|
||||||
|
graphics_system_->command_processor());
|
||||||
|
auto draw_batcher = command_processor->draw_batcher();
|
||||||
|
|
||||||
|
return draw_batcher->QueryTFBSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t QueryVSOutputElementSize() override {
|
||||||
|
// vec4 always has 4 elements.
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QueryVSOutput(void* buffer, size_t size) override {
|
||||||
|
auto command_processor = static_cast<GL4CommandProcessor*>(
|
||||||
|
graphics_system_->command_processor());
|
||||||
|
auto draw_batcher = command_processor->draw_batcher();
|
||||||
|
|
||||||
|
return draw_batcher->ReadbackTFB(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Setup() override {
|
||||||
|
if (!TraceViewer::Setup()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable TFB
|
||||||
|
auto command_processor = static_cast<GL4CommandProcessor*>(
|
||||||
|
graphics_system_->command_processor());
|
||||||
|
auto draw_batcher = command_processor->draw_batcher();
|
||||||
|
draw_batcher->set_tfb_enabled(true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
int trace_viewer_main(const std::vector<std::wstring>& args) {
|
int trace_viewer_main(const std::vector<std::wstring>& args) {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#include "third_party/half/include/half.hpp"
|
||||||
#include "third_party/imgui/imgui.h"
|
#include "third_party/imgui/imgui.h"
|
||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
@ -680,7 +681,7 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
const Shader::VertexBinding& vertex_binding,
|
const Shader::VertexBinding& vertex_binding,
|
||||||
const xe_gpu_vertex_fetch_t* fetch) {
|
const xe_gpu_vertex_fetch_t* fetch) {
|
||||||
const uint8_t* addr = memory_->TranslatePhysical(fetch->address << 2);
|
const uint8_t* addr = memory_->TranslatePhysical(fetch->address << 2);
|
||||||
uint32_t vertex_count = (fetch->size * 4) / vertex_binding.stride_words;
|
uint32_t vertex_count = fetch->size / vertex_binding.stride_words;
|
||||||
int column_count = 0;
|
int column_count = 0;
|
||||||
for (const auto& attrib : vertex_binding.attributes) {
|
for (const auto& attrib : vertex_binding.attributes) {
|
||||||
switch (attrib.fetch_instr.attributes.data_format) {
|
switch (attrib.fetch_instr.attributes.data_format) {
|
||||||
|
@ -715,9 +716,9 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::BeginChild("#indices", ImVec2(0, 300));
|
ImGui::BeginChild("#indices", ImVec2(0, 300));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10, 0));
|
||||||
int display_start, display_end;
|
int display_start, display_end;
|
||||||
ImGui::CalcListClipping(1 + vertex_count, ImGui::GetTextLineHeight(),
|
ImGui::CalcListClipping(vertex_count, ImGui::GetTextLineHeight(),
|
||||||
&display_start, &display_end);
|
&display_start, &display_end);
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
||||||
(display_start)*ImGui::GetTextLineHeight());
|
(display_start)*ImGui::GetTextLineHeight());
|
||||||
|
@ -789,16 +790,25 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_32_FLOAT:
|
case VertexFormat::k_32_FLOAT:
|
||||||
ImGui::Text("%.2f", LOADEL(float, 0));
|
ImGui::Text("%.3f", LOADEL(float, 0));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_16_16:
|
case VertexFormat::k_16_16: {
|
||||||
case VertexFormat::k_16_16_FLOAT:
|
auto e0 = LOADEL(uint32_t, 0);
|
||||||
ImGui::Text("??");
|
ImGui::Text("%.4X", (e0 >> 16) & 0xFFFF);
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("??");
|
ImGui::Text("%.4X", (e0 >> 0) & 0xFFFF);
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
} break;
|
||||||
|
case VertexFormat::k_16_16_FLOAT: {
|
||||||
|
auto e0 = LOADEL(uint32_t, 0);
|
||||||
|
ImGui::Text("%.2f",
|
||||||
|
half_float::detail::half2float((e0 >> 16) & 0xFFFF));
|
||||||
|
ImGui::NextColumn();
|
||||||
|
ImGui::Text("%.2f",
|
||||||
|
half_float::detail::half2float((e0 >> 0) & 0xFFFF));
|
||||||
|
ImGui::NextColumn();
|
||||||
|
} break;
|
||||||
case VertexFormat::k_32_32:
|
case VertexFormat::k_32_32:
|
||||||
ImGui::Text("%.8X", LOADEL(uint32_t, 0));
|
ImGui::Text("%.8X", LOADEL(uint32_t, 0));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
|
@ -806,9 +816,9 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_32_32_FLOAT:
|
case VertexFormat::k_32_32_FLOAT:
|
||||||
ImGui::Text("%.2f", LOADEL(float, 0));
|
ImGui::Text("%.3f", LOADEL(float, 0));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", LOADEL(float, 1));
|
ImGui::Text("%.3f", LOADEL(float, 1));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_10_11_11:
|
case VertexFormat::k_10_11_11:
|
||||||
|
@ -821,19 +831,19 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_32_32_32_FLOAT:
|
case VertexFormat::k_32_32_32_FLOAT:
|
||||||
ImGui::Text("%.2f", LOADEL(float, 0));
|
ImGui::Text("%.3f", LOADEL(float, 0));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", LOADEL(float, 1));
|
ImGui::Text("%.3f", LOADEL(float, 1));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", LOADEL(float, 2));
|
ImGui::Text("%.3f", LOADEL(float, 2));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_8_8_8_8:
|
case VertexFormat::k_8_8_8_8:
|
||||||
ImGui::Text("%.8X", LOADEL(uint32_t, 0));
|
ImGui::Text("%.8X", LOADEL(uint32_t, 0));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_2_10_10_10:
|
case VertexFormat::k_2_10_10_10: {
|
||||||
case VertexFormat::k_16_16_16_16:
|
auto e0 = LOADEL(uint32_t, 0);
|
||||||
ImGui::Text("??");
|
ImGui::Text("??");
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("??");
|
ImGui::Text("??");
|
||||||
|
@ -842,7 +852,19 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("??");
|
ImGui::Text("??");
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
} break;
|
||||||
|
case VertexFormat::k_16_16_16_16: {
|
||||||
|
auto e0 = LOADEL(uint32_t, 0);
|
||||||
|
auto e1 = LOADEL(uint32_t, 1);
|
||||||
|
ImGui::Text("%.4X", (e0 >> 16) & 0xFFFF);
|
||||||
|
ImGui::NextColumn();
|
||||||
|
ImGui::Text("%.4X", (e0 >> 0) & 0xFFFF);
|
||||||
|
ImGui::NextColumn();
|
||||||
|
ImGui::Text("%.4X", (e1 >> 16) & 0xFFFF);
|
||||||
|
ImGui::NextColumn();
|
||||||
|
ImGui::Text("%.4X", (e1 >> 0) & 0xFFFF);
|
||||||
|
ImGui::NextColumn();
|
||||||
|
} break;
|
||||||
case VertexFormat::k_32_32_32_32:
|
case VertexFormat::k_32_32_32_32:
|
||||||
ImGui::Text("%.8X", LOADEL(uint32_t, 0));
|
ImGui::Text("%.8X", LOADEL(uint32_t, 0));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
|
@ -853,24 +875,30 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
ImGui::Text("%.8X", LOADEL(uint32_t, 3));
|
ImGui::Text("%.8X", LOADEL(uint32_t, 3));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::k_16_16_16_16_FLOAT:
|
case VertexFormat::k_16_16_16_16_FLOAT: {
|
||||||
ImGui::Text("??");
|
auto e0 = LOADEL(uint32_t, 0);
|
||||||
|
auto e1 = LOADEL(uint32_t, 1);
|
||||||
|
ImGui::Text("%.2f",
|
||||||
|
half_float::detail::half2float((e0 >> 16) & 0xFFFF));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("??");
|
ImGui::Text("%.2f",
|
||||||
|
half_float::detail::half2float((e0 >> 0) & 0xFFFF));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("??");
|
ImGui::Text("%.2f",
|
||||||
|
half_float::detail::half2float((e1 >> 16) & 0xFFFF));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("??");
|
ImGui::Text("%.2f",
|
||||||
|
half_float::detail::half2float((e1 >> 0) & 0xFFFF));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
} break;
|
||||||
case VertexFormat::k_32_32_32_32_FLOAT:
|
case VertexFormat::k_32_32_32_32_FLOAT:
|
||||||
ImGui::Text("%.2f", LOADEL(float, 0));
|
ImGui::Text("%.3f", LOADEL(float, 0));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", LOADEL(float, 1));
|
ImGui::Text("%.3f", LOADEL(float, 1));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", LOADEL(float, 2));
|
ImGui::Text("%.3f", LOADEL(float, 2));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", LOADEL(float, 3));
|
ImGui::Text("%.3f", LOADEL(float, 3));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
break;
|
break;
|
||||||
case VertexFormat::kUndefined:
|
case VertexFormat::kUndefined:
|
||||||
|
@ -1274,6 +1302,8 @@ void TraceViewer::DrawStateUI() {
|
||||||
static_cast<ColorRenderTargetFormat>((color_info[i] >> 16) & 0xF);
|
static_cast<ColorRenderTargetFormat>((color_info[i] >> 16) & 0xF);
|
||||||
ImVec2 button_size(256, 256);
|
ImVec2 button_size(256, 256);
|
||||||
if (write_mask) {
|
if (write_mask) {
|
||||||
|
// FIXME: Valid color targets with alpha=0 don't show up.
|
||||||
|
|
||||||
auto color_target = GetColorRenderTarget(surface_pitch, surface_msaa,
|
auto color_target = GetColorRenderTarget(surface_pitch, surface_msaa,
|
||||||
color_base, color_format);
|
color_base, color_format);
|
||||||
if (ImGui::ImageButton(ImTextureID(color_target), button_size,
|
if (ImGui::ImageButton(ImTextureID(color_target), button_size,
|
||||||
|
@ -1373,6 +1403,55 @@ void TraceViewer::DrawStateUI() {
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
|
if (ImGui::CollapsingHeader("Vertex Shader Output")) {
|
||||||
|
auto size = QueryVSOutputSize();
|
||||||
|
auto el_size = QueryVSOutputElementSize();
|
||||||
|
if (size > 0) {
|
||||||
|
std::vector<float> vertices;
|
||||||
|
vertices.resize(size / 4);
|
||||||
|
QueryVSOutput(vertices.data(), size);
|
||||||
|
|
||||||
|
ImGui::Text("%d output vertices", vertices.size());
|
||||||
|
ImGui::SameLine();
|
||||||
|
static bool normalize = false;
|
||||||
|
ImGui::Checkbox("Normalize", &normalize);
|
||||||
|
|
||||||
|
ImGui::BeginChild("#vsvertices", ImVec2(0, 300));
|
||||||
|
|
||||||
|
int display_start, display_end;
|
||||||
|
ImGui::CalcListClipping(int(vertices.size() / 4),
|
||||||
|
ImGui::GetTextLineHeight(), &display_start,
|
||||||
|
&display_end);
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
||||||
|
(display_start)*ImGui::GetTextLineHeight());
|
||||||
|
|
||||||
|
ImGui::Columns(int(el_size), "#vsvertices", true);
|
||||||
|
for (size_t i = display_start; i < display_end; i++) {
|
||||||
|
size_t start_vtx = i * el_size;
|
||||||
|
float verts[4] = {vertices[start_vtx], vertices[start_vtx + 1],
|
||||||
|
vertices[start_vtx + 2], vertices[start_vtx + 3]};
|
||||||
|
assert_true(el_size <= xe::countof(verts));
|
||||||
|
if (normalize) {
|
||||||
|
for (int j = 0; j < el_size; j++) {
|
||||||
|
verts[j] /= verts[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < el_size; j++) {
|
||||||
|
ImGui::Text("%.3f", verts[j]);
|
||||||
|
ImGui::NextColumn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Columns(1);
|
||||||
|
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
||||||
|
((vertices.size() / 4) - display_end) *
|
||||||
|
ImGui::GetTextLineHeight());
|
||||||
|
ImGui::EndChild();
|
||||||
|
} else {
|
||||||
|
ImGui::Text("No vertex shader output");
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ImGui::CollapsingHeader("Pixel Shader")) {
|
if (ImGui::CollapsingHeader("Pixel Shader")) {
|
||||||
ShaderDisplayType shader_display_type = DrawShaderTypeUI();
|
ShaderDisplayType shader_display_type = DrawShaderTypeUI();
|
||||||
ImGui::BeginChild("#pixel_shader_text", ImVec2(0, 400));
|
ImGui::BeginChild("#pixel_shader_text", ImVec2(0, 400));
|
||||||
|
@ -1507,7 +1586,7 @@ void TraceViewer::DrawStateUI() {
|
||||||
ImGui::TextColored(kColorError, "ERROR: no vertex shader set");
|
ImGui::TextColored(kColorError, "ERROR: no vertex shader set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::CollapsingHeader("Textures")) {
|
if (ImGui::CollapsingHeader("Pixel Textures")) {
|
||||||
auto shader = command_processor->active_pixel_shader();
|
auto shader = command_processor->active_pixel_shader();
|
||||||
if (shader) {
|
if (shader) {
|
||||||
const auto& texture_bindings = shader->texture_bindings();
|
const auto& texture_bindings = shader->texture_bindings();
|
||||||
|
|
|
@ -54,6 +54,12 @@ class TraceViewer {
|
||||||
virtual uintptr_t GetTextureEntry(const TextureInfo& texture_info,
|
virtual uintptr_t GetTextureEntry(const TextureInfo& texture_info,
|
||||||
const SamplerInfo& sampler_info) = 0;
|
const SamplerInfo& sampler_info) = 0;
|
||||||
|
|
||||||
|
virtual size_t QueryVSOutputSize() = 0;
|
||||||
|
virtual size_t QueryVSOutputElementSize() = 0;
|
||||||
|
virtual bool QueryVSOutput(void* buffer, size_t size) = 0;
|
||||||
|
|
||||||
|
virtual bool Setup();
|
||||||
|
|
||||||
std::unique_ptr<xe::ui::Loop> loop_;
|
std::unique_ptr<xe::ui::Loop> loop_;
|
||||||
std::unique_ptr<xe::ui::Window> window_;
|
std::unique_ptr<xe::ui::Window> window_;
|
||||||
std::unique_ptr<Emulator> emulator_;
|
std::unique_ptr<Emulator> emulator_;
|
||||||
|
@ -68,7 +74,6 @@ class TraceViewer {
|
||||||
kHostDisasm,
|
kHostDisasm,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Setup();
|
|
||||||
bool Load(std::wstring trace_file_path);
|
bool Load(std::wstring trace_file_path);
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue