DrawContext: Initial implementation of dynamic UBO support

This commit is contained in:
Henrik Rydgard 2017-02-08 12:26:48 +01:00
parent 29739c8c91
commit fe1b593c15
6 changed files with 164 additions and 36 deletions

View file

@ -184,10 +184,9 @@ static const std::vector<ShaderSource> vsCol = {
}
};
static const UniformBufferDesc vsColBuf { { { 0, UniformType::MATRIX4X4, 0 } } };
struct VsColUB {
float WorldViewProj[16];
};
const UniformBufferDesc vsColBufDesc { sizeof(VsColUB), {
{ "WorldViewProj", 0, -1, UniformType::MATRIX4X4, 0 }
} };
static const std::vector<ShaderSource> vsTexCol = {
{ ShaderLanguage::GLSL_ES_200,
@ -248,10 +247,9 @@ static const std::vector<ShaderSource> vsTexCol = {
}
};
static const UniformBufferDesc vsTexColDesc{ { { 0, UniformType::MATRIX4X4, 0 } } };
struct VsTexColUB {
float WorldViewProj[16];
};
const UniformBufferDesc vsTexColBufDesc{ sizeof(VsTexColUB),{
{ "WorldViewProj", 0, -1, UniformType::MATRIX4X4, 0 }
} };
static ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const std::vector<ShaderSource> &sources) {
uint32_t supported = draw->GetSupportedShaderLanguages();

View file

@ -430,20 +430,22 @@ struct InputLayoutDesc {
class InputLayout : public RefCountedObject { };
enum class UniformType : int8_t {
FLOAT, FLOAT2, FLOAT3, FLOAT4,
FLOAT4,
MATRIX4X4,
};
// For emulation of uniform buffers on D3D9/GL
struct UniformDesc {
int16_t offset;
const char *name; // For GL
int16_t vertexReg; // For D3D
int16_t fragmentReg; // For D3D
UniformType type;
int8_t reg; // For D3D
int16_t offset;
// TODO: Support array elements etc.
};
struct UniformBufferDesc {
size_t uniformBufferSize;
std::vector<UniformDesc> uniforms;
};
@ -520,6 +522,7 @@ struct PipelineDesc {
DepthStencilState *depthStencil;
BlendState *blend;
RasterState *raster;
UniformBufferDesc *uniformDesc;
};
struct DeviceCaps {
@ -602,6 +605,10 @@ public:
virtual void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) = 0;
virtual void BindIndexBuffer(Buffer *indexBuffer, int offset) = 0;
// Only supports a single dynamic uniform buffer, for maximum compatibility with the old APIs and ease of emulation.
// More modern methods will be added later.
virtual void UpdateDynamicUniformBuffer(const void *ub, size_t size) = 0;
void BindTexture(int stage, Texture *texture) {
BindTextures(stage, 1, &texture);
} // from sampler 0 and upwards
@ -645,6 +652,8 @@ size_t DataFormatSizeInBytes(DataFormat fmt);
DrawContext *T3DCreateGLContext();
extern const UniformBufferDesc UBPresetDesc;
#ifdef _WIN32
DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, IDirect3DDevice9 *device, IDirect3DDevice9Ex *deviceEx);
DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, HWND hWnd);
@ -652,4 +661,15 @@ DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *co
DrawContext *T3DCreateVulkanContext(VulkanContext *context);
// UBs for the preset shaders
struct VsTexColUB {
float WorldViewProj[16];
};
extern const UniformBufferDesc vsTexColBufDesc;
struct VsColUB {
float WorldViewProj[16];
};
extern const UniformBufferDesc vsColBufDesc;
} // namespace Draw

View file

@ -60,9 +60,11 @@ public:
void BindTextures(int start, int count, Texture **textures) override;
void BindSamplerStates(int start, int count, SamplerState **states) override;
void BindPipeline(Pipeline *pipeline) override;
void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) override;
void BindIndexBuffer(Buffer *indexBuffer, int offset) override;
void BindPipeline(Pipeline *pipeline) override;
void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;
// Raster state
void SetScissorRect(int left, int top, int width, int height) override;
@ -503,11 +505,20 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
class D3D11Pipeline : public Pipeline {
public:
~D3D11Pipeline() {
input->Release();
blend->Release();
depth->Release();
raster->Release();
il->Release();
if (input)
input->Release();
if (blend)
blend->Release();
if (depth)
depth->Release();
if (raster)
raster->Release();
if (il)
il->Release();
if (dynamicUniforms)
dynamicUniforms->Release();
if (dynamicUniformsView)
dynamicUniformsView->Release();
}
// TODO: Refactor away these.
void SetVector(const char *name, float *value, int n) { }
@ -516,15 +527,19 @@ public:
return true;
}
D3D11InputLayout *input;
D3D11InputLayout *input = nullptr;
ID3D11InputLayout *il = nullptr;
D3D11BlendState *blend;
D3D11DepthStencilState *depth;
D3D11RasterState *raster;
ID3D11VertexShader *vs;
ID3D11PixelShader *ps;
ID3D11GeometryShader *gs;
D3D11_PRIMITIVE_TOPOLOGY topology;
D3D11BlendState *blend = nullptr;
D3D11DepthStencilState *depth = nullptr;
D3D11RasterState *raster = nullptr;
ID3D11VertexShader *vs = nullptr;
ID3D11PixelShader *ps = nullptr;
ID3D11GeometryShader *gs = nullptr;
D3D11_PRIMITIVE_TOPOLOGY topology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
size_t dynamicUniformsSize = 0;
ID3D11Buffer *dynamicUniforms = nullptr;
ID3D11ShaderResourceView *dynamicUniformsView = nullptr;
};
class D3D11Texture : public Texture {
@ -710,6 +725,22 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
dPipeline->input->AddRef();
dPipeline->raster->AddRef();
dPipeline->topology = primToD3D11[(int)desc.prim];
if (desc.uniformDesc) {
dPipeline->dynamicUniformsSize = desc.uniformDesc->uniformBufferSize;
D3D11_BUFFER_DESC bufdesc{};
bufdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bufdesc.ByteWidth = dPipeline->dynamicUniformsSize;
bufdesc.StructureByteStride = dPipeline->dynamicUniformsSize;
bufdesc.Usage = D3D11_USAGE_DYNAMIC;
bufdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
HRESULT hr = device_->CreateBuffer(&bufdesc, nullptr, &dPipeline->dynamicUniforms);
D3D11_SHADER_RESOURCE_VIEW_DESC bufview{};
bufview.Buffer.ElementOffset = 0;
bufview.Buffer.ElementWidth = dPipeline->dynamicUniformsSize;
bufview.Buffer.FirstElement = 0;
bufview.Buffer.NumElements = 1;
hr = device_->CreateShaderResourceView(dPipeline->dynamicUniforms, &bufview, &dPipeline->dynamicUniformsView);
}
std::vector<D3D11ShaderModule *> shaders;
D3D11ShaderModule *vshader = nullptr;
@ -746,6 +777,16 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
return dPipeline;
}
void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
if (curPipeline_->dynamicUniformsSize != size) {
Crash();
}
D3D11_MAPPED_SUBRESOURCE map{};
context_->Map(curPipeline_->dynamicUniforms, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
memcpy(map.pData, ub, size);
context_->Unmap(curPipeline_->dynamicUniforms, 0);
}
void D3D11DrawContext::BindPipeline(Pipeline *pipeline) {
D3D11Pipeline *dPipeline = (D3D11Pipeline *)pipeline;
if (curPipeline_ == dPipeline)

View file

@ -291,6 +291,7 @@ public:
D3D9DepthStencilState *depthStencil = nullptr;
D3D9BlendState *blend = nullptr;
D3D9RasterState *raster = nullptr;
UniformBufferDesc dynamicUniforms;
void Apply(LPDIRECT3DDEVICE9 device);
void SetVector(const char *name, float *value, int n) { vshader->SetVector(device_, name, value, n); pshader->SetVector(device_, name, value, n); }
@ -520,6 +521,8 @@ public:
curPipeline_ = (D3D9Pipeline *)pipeline;
}
void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;
// Raster state
void SetScissorRect(int left, int top, int width, int height) override;
void SetViewports(int count, Viewport *viewports) override;
@ -647,6 +650,8 @@ Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) {
pipeline->blend->AddRef();
pipeline->raster->AddRef();
pipeline->inputLayout->AddRef();
if (desc.uniformDesc)
pipeline->dynamicUniforms = *desc.uniformDesc;
return pipeline;
}
@ -811,6 +816,29 @@ Buffer *D3D9Context::CreateBuffer(size_t size, uint32_t usageFlags) {
return new D3D9Buffer(device_, size, usageFlags);
}
void D3D9Context::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
if (size != curPipeline_->dynamicUniforms.uniformBufferSize)
Crash();
for (auto &uniform : curPipeline_->dynamicUniforms.uniforms) {
int count = 0;
switch (uniform.type) {
case UniformType::FLOAT4:
count = 1;
break;
case UniformType::MATRIX4X4:
count = 4;
break;
}
const float *srcPtr = (const float *)((uint8_t *)ub + uniform.offset);
if (uniform.vertexReg != -1) {
device_->SetVertexShaderConstantF(uniform.vertexReg, srcPtr, count);
}
if (uniform.fragmentReg != -1) {
device_->SetPixelShaderConstantF(uniform.fragmentReg, srcPtr, count);
}
}
}
void D3D9Context::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) {
D3D9Buffer *buf = (D3D9Buffer *)buffer;
if (!size)

View file

@ -381,7 +381,6 @@ struct UniformInfo {
int loc_;
};
// TODO: Add Uniform Buffer support.
class OpenGLPipeline : public Pipeline, GfxResourceHolder {
public:
OpenGLPipeline() {
@ -434,9 +433,12 @@ public:
OpenGLBlendState *blend = nullptr;
OpenGLRasterState *raster = nullptr;
// TODO: Optimize by getting the locations first and putting in a custom struct
UniformBufferDesc dynamicUniforms;
private:
GLuint program_;
std::map<std::string, UniformInfo> uniforms_;
std::map<std::string, UniformInfo> uniformCache_;
};
class OpenGLFramebuffer;
@ -538,6 +540,8 @@ public:
curIBufferOffset_ = offset;
}
void UpdateDynamicUniformBuffer(const void *ub, size_t size);
// TODO: Add more sophisticated draws.
void Draw(int vertexCount, int offset) override;
void DrawIndexed(int vertexCount, int offset) override;
@ -965,6 +969,8 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
pipeline->blend->AddRef();
pipeline->raster->AddRef();
pipeline->inputLayout->AddRef();
if (desc.uniformDesc)
pipeline->dynamicUniforms = *desc.uniformDesc;
return pipeline;
} else {
delete pipeline;
@ -1046,15 +1052,15 @@ bool OpenGLPipeline::LinkShaders() {
}
int OpenGLPipeline::GetUniformLoc(const char *name) {
auto iter = uniforms_.find(name);
auto iter = uniformCache_.find(name);
int loc = -1;
if (iter != uniforms_.end()) {
if (iter != uniformCache_.end()) {
loc = iter->second.loc_;
} else {
loc = glGetUniformLocation(program_, name);
UniformInfo info;
info.loc_ = loc;
uniforms_[name] = info;
uniformCache_[name] = info;
}
return loc;
}
@ -1095,6 +1101,27 @@ void OpenGLContext::BindPipeline(Pipeline *pipeline) {
curPipeline_->raster->Apply();
}
void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
if (curPipeline_->dynamicUniforms.uniformBufferSize != size) {
Crash();
}
for (auto &uniform : curPipeline_->dynamicUniforms.uniforms) {
GLuint loc = curPipeline_->GetUniformLoc(uniform.name);
if (loc == -1)
Crash();
const float *data = (const float *)((uint8_t *)ub + uniform.offset);
switch (uniform.type) {
case UniformType::FLOAT4:
glUniform1fv(loc, 4, data);
break;
case UniformType::MATRIX4X4:
glUniformMatrix4fv(loc, 1, false, data);
break;
}
}
}
void OpenGLContext::Draw(int vertexCount, int offset) {
curVBuffers_[0]->Bind(curVBufferOffsets_[0]);
curPipeline_->inputLayout->Apply();

View file

@ -262,15 +262,18 @@ public:
class VKPipeline : public Pipeline {
public:
VKPipeline() {
// HACK! Hardcoded
uboSize_ = 16 * sizeof(float); // WorldViewProj
VKPipeline(size_t size) {
uboSize_ = size;
ubo_ = new uint8_t[uboSize_];
}
~VKPipeline() {
delete[] ubo_;
}
void SetDynamicUniformData(const void *data, size_t size) {
memcpy(ubo_, data, size);
}
// Returns the binding offset, and the VkBuffer to bind.
size_t PushUBO(VulkanPushBuffer *buf, VulkanContext *vulkan, VkBuffer *vkbuf) {
return buf->PushAligned(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment, vkbuf);
@ -289,7 +292,8 @@ public:
}
VkPipeline vkpipeline;
int stride[4];
int stride[4]{};
int dynamicUniformSize = 0;
private:
uint8_t *ubo_;
@ -376,6 +380,8 @@ public:
curIBufferOffset_ = offset;
}
void UpdateDynamicUniformBuffer(const void *ub, size_t size);
// TODO: Add more sophisticated draws.
void Draw(int vertexCount, int offset) override;
void DrawIndexed(int vertexCount, int offset) override;
@ -827,7 +833,7 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
}
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
VKPipeline *pipeline = new VKPipeline();
VKPipeline *pipeline = new VKPipeline(desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float));
VKInputLayout *input = (VKInputLayout *)desc.inputLayout;
VKBlendState *blend = (VKBlendState *)desc.blend;
VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil;
@ -900,6 +906,10 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
delete pipeline;
return nullptr;
}
if (desc.uniformDesc) {
pipeline->dynamicUniformSize = desc.uniformDesc->uniformBufferSize;
}
return pipeline;
}
@ -1112,6 +1122,10 @@ inline VkPrimitiveTopology PrimToVK(Primitive prim) {
}
}
void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
curPipeline_->SetDynamicUniformData(ub, size);
}
void VKContext::Draw(int vertexCount, int offset) {
ApplyDynamicState();