Skip to content

Commit 82dcbd9

Browse files
committed
prepare for vertex stride workaround
1 parent 99ff282 commit 82dcbd9

File tree

7 files changed

+197
-29
lines changed

7 files changed

+197
-29
lines changed

src/Cafe/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ if(ENABLE_METAL)
538538
target_sources(CemuCafe PRIVATE
539539
HW/Latte/Renderer/Metal/MetalRenderer.cpp
540540
HW/Latte/Renderer/Metal/MetalRenderer.h
541+
HW/Latte/Renderer/Metal/MetalCommon.h
541542
HW/Latte/Renderer/Metal/MetalCppImpl.cpp
542543
HW/Latte/Renderer/Metal/MetalLayer.mm
543544
HW/Latte/Renderer/Metal/MetalLayer.h
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#pragma once
2+
3+
inline size_t align(size_t size, size_t alignment)
4+
{
5+
return (size + alignment - 1) & ~(alignment - 1);
6+
}

src/Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.cpp

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h"
12
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
23
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
34

@@ -14,7 +15,7 @@ MetalBufferAllocator::~MetalBufferAllocator()
1415
MetalBufferAllocation MetalBufferAllocator::GetBufferAllocation(size_t size, size_t alignment)
1516
{
1617
// Align the size
17-
size = (size + alignment - 1) & ~(alignment - 1);
18+
size = align(size, alignment);
1819

1920
// First, try to find a free range
2021
for (uint32 i = 0; i < m_freeBufferRanges.size(); i++)
@@ -63,6 +64,65 @@ MetalBufferAllocation MetalBufferAllocator::GetBufferAllocation(size_t size, siz
6364
return allocation;
6465
}
6566

67+
MetalVertexBufferCache::~MetalVertexBufferCache()
68+
{
69+
for (uint32 i = 0; i < LATTE_MAX_VERTEX_BUFFERS; i++)
70+
{
71+
auto vertexBufferRange = m_bufferRanges[i];
72+
if (vertexBufferRange)
73+
{
74+
if (vertexBufferRange->restrideInfo.buffer)
75+
{
76+
vertexBufferRange->restrideInfo.buffer->release();
77+
}
78+
}
79+
}
80+
}
81+
82+
MetalRestridedBufferRange MetalVertexBufferCache::RestrideBufferIfNeeded(uint32 bufferIndex, size_t stride)
83+
{
84+
auto vertexBufferRange = m_bufferRanges[bufferIndex];
85+
auto& restrideInfo = vertexBufferRange->restrideInfo;
86+
87+
if (stride % 4 == 0)
88+
{
89+
// No restride needed
90+
return {nullptr, vertexBufferRange->offset};
91+
}
92+
93+
if (restrideInfo.memoryInvalidated || stride != restrideInfo.lastStride)
94+
{
95+
// TODO: restride
96+
throw std::runtime_error("restride needed");
97+
98+
restrideInfo.memoryInvalidated = false;
99+
restrideInfo.lastStride = stride;
100+
}
101+
102+
// TODO: remove
103+
throw std::runtime_error("restride unimplemented");
104+
105+
return {restrideInfo.buffer, 0};
106+
}
107+
108+
void MetalVertexBufferCache::MemoryRangeChanged(size_t offset, size_t size)
109+
{
110+
for (uint32 i = 0; i < LATTE_MAX_VERTEX_BUFFERS; i++)
111+
{
112+
auto vertexBufferRange = m_bufferRanges[i];
113+
if (vertexBufferRange)
114+
{
115+
if ((offset < vertexBufferRange->offset && (offset + size) < (vertexBufferRange->offset + vertexBufferRange->size)) ||
116+
(offset > vertexBufferRange->offset && (offset + size) > (vertexBufferRange->offset + vertexBufferRange->size)))
117+
{
118+
continue;
119+
}
120+
121+
vertexBufferRange->restrideInfo.memoryInvalidated = true;
122+
}
123+
}
124+
}
125+
66126
MetalMemoryManager::~MetalMemoryManager()
67127
{
68128
if (m_bufferCache)
@@ -85,7 +145,7 @@ void MetalMemoryManager::InitBufferCache(size_t size)
85145
{
86146
if (m_bufferCache)
87147
{
88-
printf("MetalMemoryManager::InitBufferCache: buffer cache already initialized\n");
148+
debug_printf("MetalMemoryManager::InitBufferCache: buffer cache already initialized\n");
89149
return;
90150
}
91151

@@ -101,18 +161,21 @@ void MetalMemoryManager::UploadToBufferCache(const void* data, size_t offset, si
101161

102162
if (!m_bufferCache)
103163
{
104-
printf("MetalMemoryManager::UploadToBufferCache: buffer cache not initialized\n");
164+
debug_printf("MetalMemoryManager::UploadToBufferCache: buffer cache not initialized\n");
105165
return;
106166
}
107167

108168
memcpy((uint8*)m_bufferCache->contents() + offset, data, size);
169+
170+
// Notify vertex buffer cache about the change
171+
m_vertexBufferCache.MemoryRangeChanged(offset, size);
109172
}
110173

111174
void MetalMemoryManager::CopyBufferCache(size_t srcOffset, size_t dstOffset, size_t size)
112175
{
113176
if (!m_bufferCache)
114177
{
115-
printf("MetalMemoryManager::CopyBufferCache: buffer cache not initialized\n");
178+
debug_printf("MetalMemoryManager::CopyBufferCache: buffer cache not initialized\n");
116179
return;
117180
}
118181

src/Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <Metal/Metal.hpp>
44

55
#include "Cafe/HW/Latte/ISA/LatteReg.h"
6+
#include "Cafe/HW/Latte/Core/LatteConst.h"
67

78
//const uint32 bufferAllocatorIndexShift = 24;
89

@@ -51,10 +52,65 @@ class MetalBufferAllocator
5152
std::vector<MetalBufferRange> m_freeBufferRanges;
5253
};
5354

55+
struct MetalRestridedBufferRange
56+
{
57+
MTL::Buffer* buffer;
58+
size_t offset;
59+
};
60+
61+
// TODO: use one big buffer for all the restrided vertex buffers?
62+
struct MetalRestrideInfo
63+
{
64+
bool memoryInvalidated = true;
65+
size_t lastStride = 0;
66+
MTL::Buffer* buffer = nullptr;
67+
};
68+
69+
struct MetalVertexBufferRange
70+
{
71+
size_t offset;
72+
size_t size;
73+
MetalRestrideInfo& restrideInfo;
74+
};
75+
76+
class MetalVertexBufferCache
77+
{
78+
public:
79+
friend class MetalMemoryManager;
80+
81+
MetalVertexBufferCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer} {}
82+
~MetalVertexBufferCache();
83+
84+
// Vertex buffer cache
85+
void TrackVertexBuffer(uint32 bufferIndex, size_t offset, size_t size, MetalRestrideInfo& restrideInfo)
86+
{
87+
m_bufferRanges[bufferIndex] = new MetalVertexBufferRange{offset, size, restrideInfo};
88+
}
89+
90+
void UntrackVertexBuffer(uint32 bufferIndex)
91+
{
92+
auto& range = m_bufferRanges[bufferIndex];
93+
if (range->restrideInfo.buffer)
94+
{
95+
range->restrideInfo.buffer->release();
96+
}
97+
range = nullptr;
98+
}
99+
100+
MetalRestridedBufferRange RestrideBufferIfNeeded(uint32 bufferIndex, size_t stride);
101+
102+
private:
103+
class MetalRenderer* m_mtlr;
104+
105+
MetalVertexBufferRange* m_bufferRanges[LATTE_MAX_VERTEX_BUFFERS] = {nullptr};
106+
107+
void MemoryRangeChanged(size_t offset, size_t size);
108+
};
109+
54110
class MetalMemoryManager
55111
{
56112
public:
57-
MetalMemoryManager(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer}, m_bufferAllocator(metalRenderer) {}
113+
MetalMemoryManager(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer}, m_bufferAllocator(metalRenderer), m_vertexBufferCache(metalRenderer) {}
58114
~MetalMemoryManager();
59115

60116
void ResetTemporaryBuffers()
@@ -90,13 +146,36 @@ class MetalMemoryManager
90146
void UploadToBufferCache(const void* data, size_t offset, size_t size);
91147
void CopyBufferCache(size_t srcOffset, size_t dstOffset, size_t size);
92148

149+
// Vertex buffer cache
150+
void TrackVertexBuffer(uint32 bufferIndex, size_t offset, size_t size, MetalRestrideInfo& restrideInfo)
151+
{
152+
m_vertexBufferCache.TrackVertexBuffer(bufferIndex, offset, size, restrideInfo);
153+
}
154+
155+
void UntrackVertexBuffer(uint32 bufferIndex)
156+
{
157+
m_vertexBufferCache.UntrackVertexBuffer(bufferIndex);
158+
}
159+
160+
MetalRestridedBufferRange RestrideBufferIfNeeded(uint32 bufferIndex, size_t stride)
161+
{
162+
auto range = m_vertexBufferCache.RestrideBufferIfNeeded(bufferIndex, stride);
163+
if (!range.buffer)
164+
{
165+
range.buffer = m_bufferCache;
166+
}
167+
168+
return range;
169+
}
170+
93171
private:
94172
class MetalRenderer* m_mtlr;
95173

96174
std::vector<uint8> m_textureUploadBuffer;
97175

98176
MetalBufferAllocator m_bufferAllocator;//s[2];
99177
//uint8 m_bufferAllocatorIndex = 0;
178+
MetalVertexBufferCache m_vertexBufferCache;
100179

101180
MTL::Buffer* m_bufferCache = nullptr;
102181
};

src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h"
12
#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h"
23
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
3-
#include "HW/Latte/Core/FetchShader.h"
4-
#include "HW/Latte/ISA/RegDefines.h"
54
#include "HW/Latte/Renderer/Metal/CachedFBOMtl.h"
65
#include "HW/Latte/Renderer/Metal/LatteToMtl.h"
76
#include "HW/Latte/Renderer/Metal/RendererShaderMtl.h"
87
#include "HW/Latte/Renderer/Metal/LatteTextureViewMtl.h"
98

9+
#include "HW/Latte/Core/FetchShader.h"
10+
#include "HW/Latte/ISA/RegDefines.h"
11+
1012
MetalPipelineCache::~MetalPipelineCache()
1113
{
1214
for (auto& pair : m_pipelineCache)
@@ -59,12 +61,7 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS
5961
uint32 bufferIndex = bufferGroup.attributeBufferIndex;
6062
uint32 bufferBaseRegisterIndex = mmSQ_VTX_ATTRIBUTE_BLOCK_START + bufferIndex * 7;
6163
uint32 bufferStride = (LatteGPUState.contextNew.GetRawView()[bufferBaseRegisterIndex + 2] >> 11) & 0xFFFF;
62-
63-
uint32 strideRemainder = bufferStride % 4;
64-
if (strideRemainder != 0)
65-
{
66-
debug_printf("vertex stride must be a multiple of 4, remainder: %u\n", strideRemainder);
67-
}
64+
bufferStride = align(bufferStride, 4);
6865

6966
auto layout = vertexDescriptor->layouts()->object(GET_MTL_VERTEX_BUFFER_INDEX(bufferIndex));
7067
layout->setStride(bufferStride);

src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include "Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.h"
77
#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h"
88
#include "Cafe/HW/Latte/Renderer/Metal/MetalDepthStencilCache.h"
9-
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
109
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
1110

1211
#include "Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h"
@@ -440,11 +439,22 @@ void MetalRenderer::bufferCache_copyStreamoutToMainBuffer(uint32 srcOffset, uint
440439

441440
void MetalRenderer::buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, uint32 size)
442441
{
443-
if (m_state.vertexBuffers[bufferIndex].offset == offset)
442+
cemu_assert_debug(bufferIndex < LATTE_MAX_VERTEX_BUFFERS);
443+
auto& buffer = m_state.vertexBuffers[bufferIndex];
444+
if (buffer.offset == offset && buffer.size == size)
444445
return;
445-
cemu_assert_debug(bufferIndex < LATTE_MAX_VERTEX_BUFFERS);
446-
m_state.vertexBuffers[bufferIndex].needsRebind = true;
447-
m_state.vertexBuffers[bufferIndex].offset = offset;
446+
447+
if (buffer.offset != INVALID_OFFSET)
448+
{
449+
m_memoryManager->UntrackVertexBuffer(bufferIndex);
450+
}
451+
452+
buffer.needsRebind = true;
453+
buffer.offset = offset;
454+
buffer.size = size;
455+
buffer.restrideInfo = {};
456+
457+
m_memoryManager->TrackVertexBuffer(bufferIndex, offset, size, buffer.restrideInfo);
448458
}
449459

450460
void MetalRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, uint32 bufferIndex, uint32 offset, uint32 size)
@@ -598,15 +608,25 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
598608
LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount);
599609

600610
// Vertex buffers
601-
for (uint8 i = 0; i < MAX_MTL_BUFFERS; i++)
602-
{
603-
auto& vertexBufferRange = m_state.vertexBuffers[i];
604-
if (vertexBufferRange.needsRebind)
611+
for (uint8 i = 0; i < MAX_MTL_BUFFERS; i++)
612+
{
613+
auto& vertexBufferRange = m_state.vertexBuffers[i];
614+
if (vertexBufferRange.offset != INVALID_OFFSET)
605615
{
606-
renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), vertexBufferRange.offset, GET_MTL_VERTEX_BUFFER_INDEX(i));
607-
vertexBufferRange.needsRebind = false;
616+
// Restride
617+
uint32 bufferBaseRegisterIndex = mmSQ_VTX_ATTRIBUTE_BLOCK_START + i * 7;
618+
uint32 bufferStride = (LatteGPUState.contextNew.GetRawView()[bufferBaseRegisterIndex + 2] >> 11) & 0xFFFF;
619+
620+
auto restridedBuffer = m_memoryManager->RestrideBufferIfNeeded(i, bufferStride);
621+
622+
// Bind
623+
if (vertexBufferRange.needsRebind)
624+
{
625+
renderCommandEncoder->setVertexBuffer(restridedBuffer.buffer, restridedBuffer.offset, GET_MTL_VERTEX_BUFFER_INDEX(i));
626+
vertexBufferRange.needsRebind = false;
627+
}
608628
}
609-
}
629+
}
610630

611631
// Uniform buffers, textures and samplers
612632
BindStageResources(renderCommandEncoder, vertexShader);
@@ -1186,10 +1206,7 @@ void MetalRenderer::RebindRenderState(MTL::RenderCommandEncoder* renderCommandEn
11861206
{
11871207
auto& vertexBufferRange = m_state.vertexBuffers[i];
11881208
if (vertexBufferRange.offset != INVALID_OFFSET)
1189-
{
1190-
renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), vertexBufferRange.offset, GET_MTL_VERTEX_BUFFER_INDEX(i));
1191-
vertexBufferRange.needsRebind = false;
1192-
}
1209+
vertexBufferRange.needsRebind = true;
11931210
}
11941211
}
11951212

src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include "Cafe/HW/Latte/Renderer/Renderer.h"
88

9+
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
10+
911
#define MAX_MTL_BUFFERS 31
1012
#define GET_MTL_VERTEX_BUFFER_INDEX(index) (MAX_MTL_BUFFERS - index - 2)
1113
// TODO: don't harcdode the support buffer binding
@@ -20,6 +22,9 @@ struct MetalBoundBuffer
2022
{
2123
bool needsRebind = false;
2224
size_t offset = INVALID_OFFSET;
25+
size_t size = 0;
26+
// Memory manager will write restride info to this variable
27+
MetalRestrideInfo restrideInfo;
2328
};
2429

2530
struct MetalState

0 commit comments

Comments
 (0)