mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-20 17:59:08 +02:00
Decode vertex data directly into the vertex pushbuffer, saving a memcpy.
This commit is contained in:
1 parent
d7ffc39d2a
commit
5d19f3dfb8
8 files changed
+93
-97
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user