Decode vertex data directly into the vertex pushbuffer, saving a memcpy.

This commit is contained in:
Henrik Rydgard committed 2016-03-20 19:35:03 +01:00
1 parent d7ffc39d2a
commit 5d19f3dfb8
8 files changed
+93 -97

No files matched your search

+1 -1
View File
@@ -31,7 +31,7 @@ using namespace std;
static const char *validationLayers[] = {
"VK_LAYER_GOOGLE_unique_objects",
"VK_LAYER_LUNARG_standard_validation",
//"VK_LAYER_LUNARG_standard_validation",
/*
"VK_LAYER_GOOGLE_threading",
"VK_LAYER_LUNARG_draw_state",
+1 -1
View File
@@ -308,7 +308,7 @@ void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI
assert(res == VK_SUCCESS);
}
void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, size_t offset, size_t rowLength) {
void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) {
VkBufferImageCopy copy_region = {};
copy_region.bufferOffset = offset;
copy_region.bufferRowLength = (uint32_t)rowLength;
+1 -1
View File
@@ -27,7 +27,7 @@ public:
// Fast uploads from buffer. Mipmaps supported. Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip.
void CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, size_t offset, size_t rowLength); // rowLength is in pixels
void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
void EndCreate();
int GetNumMips() const { return numMips_; }
void Destroy();
+2 -2
View File
@@ -86,9 +86,9 @@ public:
}
// "Zero-copy" variant - you can write the data directly as you compute it.
void *Push(size_t size, size_t *bindOffset, VkBuffer *vkbuf) {
void *Push(size_t size, uint32_t *bindOffset, VkBuffer *vkbuf) {
size_t off = Allocate(size, vkbuf);
*bindOffset = off;
*bindOffset = (uint32_t)off;
return writePtr_ + off;
}
+86 -78
View File
@@ -64,7 +64,6 @@ enum {
DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
:
vulkan_(vulkan),
decodedVerts_(0),
prevPrim_(GE_PRIM_INVALID),
lastVTypeID_(-1),
pipelineManager_(nullptr),
@@ -72,7 +71,6 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
framebufferManager_(nullptr),
numDrawCalls(0),
vertexCountInDrawCalls(0),
decodeCounter_(0),
fboTexNeedBind_(false),
fboTexBound_(false),
curFrame_(0) {
@@ -298,9 +296,87 @@ void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim,
}
}
void DrawEngineVulkan::DecodeVerts() {
for (; decodeCounter_ < numDrawCalls; decodeCounter_++) {
DecodeVertsStep(decoded);
void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf) {
int decodedVerts = 0;
u8 *dest = decoded;
// Figure out how much pushbuffer space we need to allocate.
if (push) {
int vertsToDecode = 0;
for (int i = 0; i < numDrawCalls; i++) {
const DeferredDrawCall &dc = drawCalls[i];
if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
vertsToDecode += dc.indexUpperBound - dc.indexLowerBound + 1;
} else {
vertsToDecode += dc.vertexCount;
}
}
dest = (u8 *)push->Push(vertsToDecode * dec_->GetDecVtxFmt().stride, bindOffset, vkbuf);
}
for (int i = 0; i < numDrawCalls; i++) {
const DeferredDrawCall &dc = drawCalls[i];
indexGen.SetIndex(decodedVerts);
int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound;
void *inds = dc.inds;
if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
// Decode the verts and apply morphing. Simple.
dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride,
dc.verts, indexLowerBound, indexUpperBound);
decodedVerts += indexUpperBound - indexLowerBound + 1;
indexGen.AddPrim(dc.prim, dc.vertexCount);
} else {
// It's fairly common that games issue long sequences of PRIM calls, with differing
// inds pointer but the same base vertex pointer. We'd like to reuse vertices between
// these as much as possible, so we make sure here to combine as many as possible
// into one nice big drawcall, sharing data.
// 1. Look ahead to find the max index, only looking as "matching" drawcalls.
// Expand the lower and upper bounds as we go.
int lastMatch = i;
const int total = numDrawCalls;
for (int j = i + 1; j < total; ++j) {
if (drawCalls[j].verts != dc.verts)
break;
indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound);
indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound);
lastMatch = j;
}
// 2. Loop through the drawcalls, translating indices as we go.
switch (dc.indexType) {
case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u8 *)drawCalls[j].inds, indexLowerBound);
}
break;
case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u16 *)drawCalls[j].inds, indexLowerBound);
}
break;
}
const int vertexCount = indexUpperBound - indexLowerBound + 1;
// This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes.
if (decodedVerts + vertexCount > VERTEX_BUFFER_MAX) {
return;
}
// 3. Decode that range of vertex data.
dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride,
dc.verts, indexLowerBound, indexUpperBound);
decodedVerts += vertexCount;
// 4. Advance indexgen vertex counter.
indexGen.Advance(vertexCount);
i = lastMatch;
}
}
// Sanity check
if (indexGen.Prim() < 0) {
@@ -310,73 +386,6 @@ void DrawEngineVulkan::DecodeVerts() {
}
}
void DrawEngineVulkan::DecodeVertsStep(u8 *decoded) {
const int i = decodeCounter_;
const DeferredDrawCall &dc = drawCalls[i];
indexGen.SetIndex(decodedVerts_);
int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound;
u32 indexType = dc.indexType;
void *inds = dc.inds;
if (indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
// Decode the verts and apply morphing. Simple.
dec_->DecodeVerts(decoded + decodedVerts_ * (int)dec_->GetDecVtxFmt().stride,
dc.verts, indexLowerBound, indexUpperBound);
decodedVerts_ += indexUpperBound - indexLowerBound + 1;
indexGen.AddPrim(dc.prim, dc.vertexCount);
} else {
// It's fairly common that games issue long sequences of PRIM calls, with differing
// inds pointer but the same base vertex pointer. We'd like to reuse vertices between
// these as much as possible, so we make sure here to combine as many as possible
// into one nice big drawcall, sharing data.
// 1. Look ahead to find the max index, only looking as "matching" drawcalls.
// Expand the lower and upper bounds as we go.
int lastMatch = i;
const int total = numDrawCalls;
for (int j = i + 1; j < total; ++j) {
if (drawCalls[j].verts != dc.verts)
break;
indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound);
indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound);
lastMatch = j;
}
// 2. Loop through the drawcalls, translating indices as we go.
switch (indexType) {
case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u8 *)drawCalls[j].inds, indexLowerBound);
}
break;
case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u16 *)drawCalls[j].inds, indexLowerBound);
}
break;
}
const int vertexCount = indexUpperBound - indexLowerBound + 1;
// This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes.
if (decodedVerts_ + vertexCount > VERTEX_BUFFER_MAX) {
return;
}
// 3. Decode that range of vertex data.
dec_->DecodeVerts(decoded + decodedVerts_ * (int)dec_->GetDecVtxFmt().stride,
dc.verts, indexLowerBound, indexUpperBound);
decodedVerts_ += vertexCount;
// 4. Advance indexgen vertex counter.
indexGen.Advance(vertexCount);
decodeCounter_ = lastMatch;
}
}
inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) {
// Switch to u32 units.
const u32 *p = (const u32 *)ptr;
@@ -520,7 +529,9 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
int maxIndex = 0;
bool useElements = true;
DecodeVerts();
// Decode directly into the pushbuffer
VkBuffer vbuf;
DecodeVerts(frame->pushVertex, &vbOffset, &vbuf);
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
useElements = !indexGen.SeenOnlyPurePrims();
vertexCount = indexGen.VertexCount();
@@ -578,8 +589,6 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
int stride = dec_->GetDecVtxFmt().stride;
VkBuffer vbuf;
vbOffset = (uint32_t)frame->pushVertex->Push(decoded, indexGen.MaxIndex() * stride, &vbuf);
VkDeviceSize offsets[1] = { vbOffset };
if (useElements) {
@@ -594,7 +603,8 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
vkCmdDraw(cmd_, vertexCount, 1, 0, 0);
}
} else {
DecodeVerts();
// Decode to "decoded"
DecodeVerts(nullptr, nullptr, nullptr);
bool hasColor = (lastVTypeID_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
@@ -746,10 +756,8 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
gpuStats.numVertsSubmitted += vertexCountInDrawCalls;
indexGen.Reset();
decodedVerts_ = 0;
numDrawCalls = 0;
vertexCountInDrawCalls = 0;
decodeCounter_ = 0;
prevPrim_ = GE_PRIM_INVALID;
gstate_c.vertexFullAlpha = true;
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
+1 -11
View File
@@ -123,12 +123,6 @@ public:
DoFlush(cmd);
}
void FinishDeferred() {
if (!numDrawCalls)
return;
DecodeVerts();
}
bool IsCodePtrVertexDecoder(const u8 *ptr) const;
void DispatchFlush() override { Flush(cmd_); }
@@ -148,8 +142,7 @@ public:
void EndFrame();
private:
void DecodeVerts();
void DecodeVertsStep(u8 *decoded);
void DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf);
void DoFlush(VkCommandBuffer cmd);
VkDescriptorSet GetDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone);
@@ -213,7 +206,6 @@ private:
// Vertex collector state
IndexGenerator indexGen;
int decodedVerts_;
GEPrimitiveType prevPrim_;
u32 lastVTypeID_;
@@ -235,8 +227,6 @@ private:
int numDrawCalls;
int vertexCountInDrawCalls;
int decodeCounter_;
bool fboTexNeedBind_;
bool fboTexBound_;
};
-2
View File
@@ -672,8 +672,6 @@ void GPU_Vulkan::FastRunLoop(DisplayList &list) {
}
void GPU_Vulkan::FinishDeferred() {
// This finishes reading any vertex data that is pending.
drawEngine_.FinishDeferred();
}
void GPU_Vulkan::ProcessEvent(GPUEvent ev) {
+1 -1
View File
@@ -1481,7 +1481,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) {
int bpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2;
int stride = (mipWidth * bpp + 15) & ~15;
int size = stride * mipHeight;
size_t bufferOffset;
uint32_t bufferOffset;
VkBuffer texBuf;
void *data = uploadBuffer->Push(size, &bufferOffset, &texBuf);
LoadTextureLevel(*entry, (uint8_t *)data, stride, i, replaceImages, scaleFactor, dstFmt);