MultiDrawIndirect draw batching - now down to <20us per draw.
This commit is contained in:
230
src/xenia/gpu/gl4/draw_batcher.h
Normal file
230
src/xenia/gpu/gl4/draw_batcher.h
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_GL4_GL4_STATE_DATA_BUILDER_H_
|
||||
#define XENIA_GPU_GL4_GL4_STATE_DATA_BUILDER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/gpu/gl4/circular_buffer.h>
|
||||
#include <xenia/gpu/gl4/gl_context.h>
|
||||
#include <xenia/gpu/gl4/gl4_shader.h>
|
||||
#include <xenia/gpu/register_file.h>
|
||||
#include <xenia/gpu/xenos.h>
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace gl4 {
|
||||
|
||||
union float4 {
|
||||
float v[4];
|
||||
struct {
|
||||
float x, y, z, w;
|
||||
};
|
||||
};
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct DrawArraysIndirectCommand {
|
||||
GLuint count;
|
||||
GLuint instance_count;
|
||||
GLuint first_index;
|
||||
GLuint base_instance;
|
||||
};
|
||||
struct DrawElementsIndirectCommand {
|
||||
GLuint count;
|
||||
GLuint instance_count;
|
||||
GLuint first_index;
|
||||
GLint base_vertex;
|
||||
GLuint base_instance;
|
||||
};
|
||||
struct BindlessPtrNV {
|
||||
GLuint index;
|
||||
GLuint reserved_zero;
|
||||
GLuint64 address;
|
||||
GLuint64 length;
|
||||
};
|
||||
struct DrawArraysIndirectBindlessCommandNV {
|
||||
DrawArraysIndirectCommand cmd;
|
||||
// NOTE: the spec is wrong here. For fucks sake.
|
||||
// GLuint reserved_zero;
|
||||
BindlessPtrNV vertex_buffers[8];
|
||||
};
|
||||
struct DrawElementsIndirectBindlessCommandNV {
|
||||
DrawElementsIndirectCommand cmd;
|
||||
GLuint reserved_zero;
|
||||
BindlessPtrNV index_buffer;
|
||||
BindlessPtrNV vertex_buffers[8];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class DrawBatcher {
|
||||
public:
|
||||
enum class FlushMode {
|
||||
kMakeCoherent,
|
||||
kStateChange,
|
||||
kReconfigure,
|
||||
};
|
||||
|
||||
DrawBatcher(RegisterFile* register_file);
|
||||
|
||||
bool Initialize(CircularBuffer* array_data_buffer);
|
||||
void Shutdown();
|
||||
|
||||
PrimitiveType prim_type() const { return batch_state_.prim_type; }
|
||||
|
||||
void set_window_offset(uint32_t x, uint32_t y) {
|
||||
active_draw_.header->window_offset.x = float(x);
|
||||
active_draw_.header->window_offset.y = float(y);
|
||||
}
|
||||
void set_window_scissor(uint32_t left, uint32_t top, uint32_t right,
|
||||
uint32_t bottom) {
|
||||
active_draw_.header->window_scissor.x = float(left);
|
||||
active_draw_.header->window_scissor.y = float(top);
|
||||
active_draw_.header->window_scissor.z = float(right);
|
||||
active_draw_.header->window_scissor.w = float(bottom);
|
||||
}
|
||||
void set_window_scalar(float width_scalar, float height_scalar) {
|
||||
active_draw_.header->window_offset.z = width_scalar;
|
||||
active_draw_.header->window_offset.w = height_scalar;
|
||||
}
|
||||
void set_viewport_offset(float offset_x, float offset_y, float offset_z) {
|
||||
active_draw_.header->viewport_offset.x = offset_x;
|
||||
active_draw_.header->viewport_offset.y = offset_y;
|
||||
active_draw_.header->viewport_offset.z = offset_z;
|
||||
}
|
||||
void set_viewport_scale(float scale_x, float scale_y, float scale_z) {
|
||||
active_draw_.header->viewport_scale.x = scale_x;
|
||||
active_draw_.header->viewport_scale.y = scale_y;
|
||||
active_draw_.header->viewport_scale.z = scale_z;
|
||||
}
|
||||
void set_vtx_fmt(float xy, float z, float w) {
|
||||
active_draw_.header->vtx_fmt.x = xy;
|
||||
active_draw_.header->vtx_fmt.y = xy;
|
||||
active_draw_.header->vtx_fmt.z = z;
|
||||
active_draw_.header->vtx_fmt.w = w;
|
||||
}
|
||||
void set_alpha_test(bool enabled, uint32_t func, float ref) {
|
||||
active_draw_.header->alpha_test.x = enabled ? 1.0f : 0.0f;
|
||||
active_draw_.header->alpha_test.y = float(func);
|
||||
active_draw_.header->alpha_test.z = ref;
|
||||
}
|
||||
void set_texture_sampler(int index, GLuint64 handle) {
|
||||
active_draw_.header->texture_samplers[index] = handle;
|
||||
}
|
||||
void set_index_buffer(const CircularBuffer::Allocation& allocation) {
|
||||
if (has_bindless_mdi_) {
|
||||
auto& ptr = active_draw_.draw_elements_bindless_cmd->index_buffer;
|
||||
ptr.reserved_zero = 0;
|
||||
ptr.index = 0;
|
||||
ptr.address = allocation.gpu_ptr;
|
||||
ptr.length = allocation.length;
|
||||
} else {
|
||||
// Offset is used in glDrawElements.
|
||||
auto& cmd = active_draw_.draw_elements_cmd;
|
||||
size_t index_size = batch_state_.index_type == GL_UNSIGNED_SHORT ? 2 : 4;
|
||||
cmd->first_index = GLuint(allocation.offset / index_size);
|
||||
}
|
||||
}
|
||||
void set_vertex_buffer(int index, GLsizei offset, GLsizei stride,
|
||||
const CircularBuffer::Allocation& allocation) {
|
||||
if (has_bindless_mdi_) {
|
||||
BindlessPtrNV* ptr;
|
||||
if (batch_state_.indexed) {
|
||||
ptr = &active_draw_.draw_elements_bindless_cmd->vertex_buffers[index];
|
||||
} else {
|
||||
ptr = &active_draw_.draw_arrays_bindless_cmd->vertex_buffers[index];
|
||||
}
|
||||
ptr->reserved_zero = 0;
|
||||
ptr->index = index;
|
||||
ptr->address = allocation.gpu_ptr + offset;
|
||||
ptr->length = allocation.length - offset;
|
||||
}
|
||||
}
|
||||
|
||||
bool ReconfigurePipeline(GL4Shader* vertex_shader, GL4Shader* pixel_shader,
|
||||
GLuint pipeline);
|
||||
|
||||
bool BeginDrawArrays(PrimitiveType prim_type, uint32_t index_count);
|
||||
bool BeginDrawElements(PrimitiveType prim_type, uint32_t index_count,
|
||||
xenos::IndexFormat index_format);
|
||||
void DiscardDraw();
|
||||
bool CommitDraw();
|
||||
bool Flush(FlushMode mode);
|
||||
|
||||
private:
|
||||
bool BeginDraw();
|
||||
void CopyConstants();
|
||||
|
||||
RegisterFile* register_file_;
|
||||
CircularBuffer command_buffer_;
|
||||
CircularBuffer state_buffer_;
|
||||
CircularBuffer* array_data_buffer_;
|
||||
|
||||
bool has_bindless_mdi_;
|
||||
|
||||
struct BatchState {
|
||||
bool needs_reconfigure;
|
||||
PrimitiveType prim_type;
|
||||
bool indexed;
|
||||
GLenum index_type;
|
||||
|
||||
GL4Shader* vertex_shader;
|
||||
GL4Shader* pixel_shader;
|
||||
GLuint pipeline;
|
||||
|
||||
GLsizei command_stride;
|
||||
GLsizei state_stride;
|
||||
GLsizei float_consts_offset;
|
||||
GLsizei bool_consts_offset;
|
||||
GLsizei loop_consts_offset;
|
||||
|
||||
uintptr_t command_range_start;
|
||||
uintptr_t command_range_length;
|
||||
uintptr_t state_range_start;
|
||||
uintptr_t state_range_length;
|
||||
GLsizei draw_count;
|
||||
} batch_state_;
|
||||
|
||||
// This must match GL4Shader's header.
|
||||
struct CommonHeader {
|
||||
float4 window_offset; // tx,ty,sx,sy
|
||||
float4 window_scissor; // x0,y0,x1,y1
|
||||
float4 viewport_offset; // tx,ty,tz,?
|
||||
float4 viewport_scale; // sx,sy,sz,?
|
||||
float4 vtx_fmt; //
|
||||
float4 alpha_test; // alpha test enable, func, ref, ?
|
||||
|
||||
// TODO(benvanik): pack tightly
|
||||
GLuint64 texture_samplers[32];
|
||||
|
||||
float4 float_consts[512];
|
||||
uint32_t bool_consts[8];
|
||||
uint32_t loop_consts[32];
|
||||
};
|
||||
struct {
|
||||
CircularBuffer::Allocation command_allocation;
|
||||
CircularBuffer::Allocation state_allocation;
|
||||
|
||||
union {
|
||||
DrawArraysIndirectCommand* draw_arrays_cmd;
|
||||
DrawElementsIndirectCommand* draw_elements_cmd;
|
||||
DrawArraysIndirectBindlessCommandNV* draw_arrays_bindless_cmd;
|
||||
DrawElementsIndirectBindlessCommandNV* draw_elements_bindless_cmd;
|
||||
uintptr_t command_address;
|
||||
};
|
||||
|
||||
CommonHeader* header;
|
||||
} active_draw_;
|
||||
bool draw_open_;
|
||||
};
|
||||
|
||||
} // namespace gl4
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_GPU_GL4_GL4_STATE_DATA_BUILDER_H_
|
||||
Reference in New Issue
Block a user