Change the API for updating buffers from the CPU to be cleaner

This commit is contained in:
Henrik Rydgard 2017-02-07 19:04:44 +01:00
parent 534a65c610
commit d78d3bb25f
7 changed files with 143 additions and 136 deletions

View File

@ -236,10 +236,10 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) {
{ x2, y2, 0, u1, v1, 0xFFFFFFFF }, // BR
{ x2, y, 0, u1, v0, 0xFFFFFFFF }, // TR
};
vdata->SubData((const uint8_t *)verts, 0, sizeof(verts));
draw_->UpdateBuffer(vdata, (const uint8_t *)verts, 0, sizeof(verts));
int indexes[] = { 0, 1, 2, 0, 2, 3 };
idata->SubData((const uint8_t *)indexes, 0, sizeof(indexes));
draw_->UpdateBuffer(idata, (const uint8_t *)indexes, 0, sizeof(indexes));
draw_->BindTexture(0, fbTex);

View File

@ -93,7 +93,7 @@ void DrawBuffer::Flush(bool set_blend_state) {
pipeline_->SetMatrix4x4("WorldViewProj", drawMatrix_.getReadPtr());
draw_->BindPipeline(pipeline_);
if (vbuf_) {
vbuf_->SubData((const uint8_t *)verts_, 0, sizeof(Vertex) * count_);
draw_->UpdateBuffer(vbuf_, (const uint8_t *)verts_, 0, sizeof(Vertex) * count_);
draw_->BindVertexBuffers(0, 1, &vbuf_, nullptr);
int offset = 0;
draw_->Draw(count_, offset);

View File

@ -388,7 +388,6 @@ public:
class Buffer : public RefCountedObject {
public:
virtual void SubData(const uint8_t *data, size_t offset, size_t size) = 0;
};
class Texture : public RefCountedObject {

View File

@ -701,12 +701,10 @@ public:
buf->Release();
if (srView)
srView->Release();
}
virtual void SubData(const uint8_t *data, size_t offset, size_t size) override {
}
ID3D11Buffer *buf;
ID3D11ShaderResourceView *srView;
size_t size;
};
Buffer *D3D11DrawContext::CreateBuffer(size_t size, uint32_t usageFlags) {
@ -720,8 +718,11 @@ Buffer *D3D11DrawContext::CreateBuffer(size_t size, uint32_t usageFlags) {
desc.BindFlags |= D3D11_BIND_INDEX_BUFFER;
if (usageFlags & UNIFORM)
desc.BindFlags |= D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.Usage = D3D11_USAGE_DYNAMIC;
b->size = size;
HRESULT hr = device_->CreateBuffer(&desc, nullptr, &b->buf);
if (FAILED(hr)) {
delete b;
@ -732,7 +733,20 @@ Buffer *D3D11DrawContext::CreateBuffer(size_t size, uint32_t usageFlags) {
}
void D3D11DrawContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size) {
Crash();
D3D11Buffer *buf = (D3D11Buffer *)buffer;
if (offset == 0 && size == buf->size) {
// Can just discard the old contents. This is only allowed for DYNAMIC buffers.
D3D11_MAPPED_SUBRESOURCE map;
context_->Map(buf->buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
memcpy(map.pData, data, size);
context_->Unmap(buf->buf, 0);
return;
}
D3D11_BOX box{};
box.left = offset;
box.right = offset + size;
context_->UpdateSubresource(buf->buf, 0, &box, data, 0, 0);
}
void D3D11DrawContext::BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) {

View File

@ -142,6 +142,8 @@ static int FormatToD3DDeclType(DataFormat type) {
}
}
class D3D9Buffer;
class D3D9DepthStencilState : public DepthStencilState {
public:
BOOL depthTestEnabled;
@ -219,58 +221,6 @@ public:
}
};
// Simulate a simple buffer type like the other backends have, use the usage flags to create the right internal type.
class D3D9Buffer : public Buffer {
public:
D3D9Buffer(LPDIRECT3DDEVICE9 device, size_t size, uint32_t flags) : vbuffer_(nullptr), ibuffer_(nullptr), maxSize_(size) {
if (flags & BufferUsageFlag::INDEXDATA) {
DWORD usage = D3DUSAGE_DYNAMIC;
device->CreateIndexBuffer((UINT)size, usage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &ibuffer_, NULL);
}
else {
DWORD usage = D3DUSAGE_DYNAMIC;
device->CreateVertexBuffer((UINT)size, usage, 0, D3DPOOL_DEFAULT, &vbuffer_, NULL);
}
}
virtual ~D3D9Buffer() override {
if (ibuffer_) {
ibuffer_->Release();
}
if (vbuffer_) {
vbuffer_->Release();
}
}
void SubData(const uint8_t *data, size_t offset, size_t size) override {
if (!size)
return;
if (offset + size > maxSize_) {
ELOG("Can't SubData with bigger size than buffer was created with");
return;
}
if (vbuffer_) {
void *ptr;
HRESULT res = vbuffer_->Lock((UINT)offset, (UINT)size, &ptr, D3DLOCK_DISCARD);
if (!FAILED(res)) {
memcpy(ptr, data, size);
vbuffer_->Unlock();
}
} else if (ibuffer_) {
void *ptr;
HRESULT res = ibuffer_->Lock((UINT)offset, (UINT)size, &ptr, D3DLOCK_DISCARD);
if (!FAILED(res)) {
memcpy(ptr, data, size);
ibuffer_->Unlock();
}
}
}
public:
LPDIRECT3DVERTEXBUFFER9 vbuffer_;
LPDIRECT3DINDEXBUFFER9 ibuffer_;
size_t maxSize_;
};
class D3D9InputLayout : public InputLayout {
public:
D3D9InputLayout(LPDIRECT3DDEVICE9 device, const InputLayoutDesc &desc);
@ -831,12 +781,59 @@ D3D9InputLayout::D3D9InputLayout(LPDIRECT3DDEVICE9 device, const InputLayoutDesc
delete[] elements;
}
// Simulate a simple buffer type like the other backends have, use the usage flags to create the right internal type.
class D3D9Buffer : public Buffer {
public:
D3D9Buffer(LPDIRECT3DDEVICE9 device, size_t size, uint32_t flags) : vbuffer_(nullptr), ibuffer_(nullptr), maxSize_(size) {
if (flags & BufferUsageFlag::INDEXDATA) {
DWORD usage = D3DUSAGE_DYNAMIC;
device->CreateIndexBuffer((UINT)size, usage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &ibuffer_, NULL);
} else {
DWORD usage = D3DUSAGE_DYNAMIC;
device->CreateVertexBuffer((UINT)size, usage, 0, D3DPOOL_DEFAULT, &vbuffer_, NULL);
}
}
virtual ~D3D9Buffer() override {
if (ibuffer_) {
ibuffer_->Release();
}
if (vbuffer_) {
vbuffer_->Release();
}
}
LPDIRECT3DVERTEXBUFFER9 vbuffer_;
LPDIRECT3DINDEXBUFFER9 ibuffer_;
size_t maxSize_;
};
Buffer *D3D9Context::CreateBuffer(size_t size, uint32_t usageFlags) {
return new D3D9Buffer(device_, size, usageFlags);
}
void D3D9Context::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size) {
Crash();
D3D9Buffer *buf = (D3D9Buffer *)buffer;
if (!size)
return;
if (offset + size > buf->maxSize_) {
ELOG("Can't SubData with bigger size than buffer was created with");
return;
}
if (buf->vbuffer_) {
void *ptr;
HRESULT res = buf->vbuffer_->Lock((UINT)offset, (UINT)size, &ptr, D3DLOCK_DISCARD);
if (!FAILED(res)) {
memcpy(ptr, data, size);
buf->vbuffer_->Unlock();
}
} else if (buf->ibuffer_) {
void *ptr;
HRESULT res = buf->ibuffer_->Lock((UINT)offset, (UINT)size, &ptr, D3DLOCK_DISCARD);
if (!FAILED(res)) {
memcpy(ptr, data, size);
buf->ibuffer_->Unlock();
}
}
}
void D3D9Pipeline::Apply(LPDIRECT3DDEVICE9 device) {

View File

@ -153,6 +153,8 @@ static const unsigned short primToGL[] = {
#endif
};
class OpenGLBuffer;
static const char *glsl_fragment_prelude =
"#ifdef GL_ES\n"
"precision mediump float;\n"
@ -270,56 +272,6 @@ public:
GLenum frontFace;
};
class OpenGLBuffer : public Buffer, GfxResourceHolder {
public:
OpenGLBuffer(size_t size, uint32_t flags) {
glGenBuffers(1, &buffer_);
target_ = (flags & BufferUsageFlag::INDEXDATA) ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
usage_ = 0;
if (flags & BufferUsageFlag::DYNAMIC)
usage_ = GL_STREAM_DRAW;
else
usage_ = GL_STATIC_DRAW;
totalSize_ = size;
glBindBuffer(target_, buffer_);
glBufferData(target_, size, NULL, usage_);
register_gl_resource_holder(this);
}
~OpenGLBuffer() override {
unregister_gl_resource_holder(this);
glDeleteBuffers(1, &buffer_);
}
void SubData(const uint8_t *data, size_t offset, size_t size) override {
Bind(0);
if (size + offset > totalSize_) {
Crash();
}
glBufferSubData(target_, offset, size, data);
}
void Bind(int offset) {
// TODO: Can't support offset using ES 2.0
glBindBuffer(target_, buffer_);
}
void GLLost() override {
buffer_ = 0;
}
void GLRestore() override {
ILOG("Recreating vertex buffer after gl_restore");
totalSize_ = 0; // Will cause a new glBufferData call. Should genBuffers again though?
glGenBuffers(1, &buffer_);
}
private:
GLuint buffer_;
GLuint target_;
GLuint usage_;
size_t totalSize_;
};
GLuint ShaderStageToOpenGL(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX: return GL_VERTEX_SHADER;
@ -935,12 +887,60 @@ RasterState *OpenGLContext::CreateRasterState(const RasterStateDesc &desc) {
return rs;
}
class OpenGLBuffer : public Buffer, GfxResourceHolder {
public:
OpenGLBuffer(size_t size, uint32_t flags) {
glGenBuffers(1, &buffer_);
target_ = (flags & BufferUsageFlag::INDEXDATA) ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
usage_ = 0;
if (flags & BufferUsageFlag::DYNAMIC)
usage_ = GL_STREAM_DRAW;
else
usage_ = GL_STATIC_DRAW;
totalSize_ = size;
glBindBuffer(target_, buffer_);
glBufferData(target_, size, NULL, usage_);
register_gl_resource_holder(this);
}
~OpenGLBuffer() override {
unregister_gl_resource_holder(this);
glDeleteBuffers(1, &buffer_);
}
void Bind(int offset) {
// TODO: Can't support offset using ES 2.0
glBindBuffer(target_, buffer_);
}
void GLLost() override {
buffer_ = 0;
}
void GLRestore() override {
ILOG("Recreating vertex buffer after gl_restore");
totalSize_ = 0; // Will cause a new glBufferData call. Should genBuffers again though?
glGenBuffers(1, &buffer_);
}
GLuint buffer_;
GLuint target_;
GLuint usage_;
size_t totalSize_;
};
Buffer *OpenGLContext::CreateBuffer(size_t size, uint32_t usageFlags) {
return new OpenGLBuffer(size, usageFlags);
}
void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size) {
OpenGLBuffer *buf = (OpenGLBuffer *)buffer;
buf->Bind(0);
if (size + offset > buf->totalSize_) {
Crash();
}
glBufferSubData(buf->target_, offset, size, data);
}
Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {

View File

@ -176,29 +176,6 @@ public:
}
};
// Very simplistic buffer that will simply copy its contents into our "pushbuffer" when it's time to draw,
// to avoid synchronization issues.
class VKBuffer : public Buffer {
public:
VKBuffer(size_t size, uint32_t flags) : dataSize_(size) {
data_ = new uint8_t[size];
}
~VKBuffer() override {
delete[] data_;
}
void SubData(const uint8_t *data, size_t offset, size_t size) override {
memcpy(data_, data_ + offset, size);
}
size_t GetSize() const { return dataSize_; }
const uint8_t *GetData() const { return data_; }
private:
uint8_t *data_;
size_t dataSize_;
};
VkShaderStageFlagBits StageToVulkan(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX: return VK_SHADER_STAGE_VERTEX_BIT;
@ -320,6 +297,7 @@ private:
};
class VKTexture;
class VKBuffer;
class VKSamplerState;
struct DescriptorSetKey {
@ -1050,12 +1028,31 @@ BlendState *VKContext::CreateBlendState(const BlendStateDesc &desc) {
return bs;
}
// Very simplistic buffer that will simply copy its contents into our "pushbuffer" when it's time to draw,
// to avoid synchronization issues.
class VKBuffer : public Buffer {
public:
VKBuffer(size_t size, uint32_t flags) : dataSize_(size) {
data_ = new uint8_t[size];
}
~VKBuffer() override {
delete[] data_;
}
size_t GetSize() const { return dataSize_; }
const uint8_t *GetData() const { return data_; }
uint8_t *data_;
size_t dataSize_;
};
Buffer *VKContext::CreateBuffer(size_t size, uint32_t usageFlags) {
return new VKBuffer(size, usageFlags);
}
void VKContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size) {
Crash();
VKBuffer *buf = (VKBuffer *)buffer;
memcpy(buf->data_ + offset, data, size);
}
void VKContext::BindTextures(int start, int count, Texture **textures) {