Skip to content

Commit

Permalink
gxm: Alloc uniform ring-buffer memory in sceGxmDraw, not reserve funcs
Browse files Browse the repository at this point in the history
- Resolves #302
- The game worked by accident, the change from #297 didn't actually
  cause the regression.
- Turns out libgxm allocated memory from ring buffers in sceGxmDraw, not
  when reserving uniform buffers. This resulted in Flood It (which
  allocated a default vertex buffer twice) to reserve 2 _separate_
  vertex buffers resulting in a uniform not being written correctly.
- Correct behavior should be that the second time a reservation happens
  in a single scene/command list, the initial buffer should be written
  instead, not a new one.
  • Loading branch information
VelocityRa committed Aug 21, 2018
1 parent 639af94 commit 5b97fe1
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 26 deletions.
8 changes: 8 additions & 0 deletions src/emulator/gxm/include/gxm/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ struct GxmStencilState {
uint32_t ref = 0; // TODO What's the default value?
};

enum class SceGxmLastReserveStatus {
Available = 0, // No reservations have happened since last allocation
Reserved = 1, // A reservation succeeded and space needs to be allocated
AvailableAgain = 2 // TODO: Not sure what's the point of this third state (why libgxm doesn't set this back to Available)
};

struct GxmContextState {
// Constant after initialisation.
emu::SceGxmContextParams params;
Expand Down Expand Up @@ -105,6 +111,8 @@ struct GxmContextState {
UniformBuffers vertex_uniform_buffers;
size_t fragment_ring_buffer_used = 0;
size_t vertex_ring_buffer_used = 0;
SceGxmLastReserveStatus fragment_last_reserve_status = SceGxmLastReserveStatus::Available;
SceGxmLastReserveStatus vertex_last_reserve_status = SceGxmLastReserveStatus::Available;

// Vertex streams.
std::array<Ptr<const void>, SCE_GXM_MAX_VERTEX_STREAMS> stream_data;
Expand Down
61 changes: 35 additions & 26 deletions src/emulator/modules/SceGxm/SceGxm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,35 @@ EXPORT(int, sceGxmDraw, SceGxmContext *context, SceGxmPrimitiveType primType, Sc
return RET_ERROR(SCE_GXM_ERROR_NOT_WITHIN_SCENE);
}

if (context->state.vertex_last_reserve_status == SceGxmLastReserveStatus::Reserved) {
const auto vertex_program = context->state.vertex_program.get(host.mem);
const auto program = vertex_program->program.get(host.mem);

const size_t size = program->default_uniform_buffer_count * 4;
const size_t next_used = context->state.vertex_ring_buffer_used + size;
assert(next_used <= context->state.params.vertexRingBufferMemSize);
if (next_used > context->state.params.vertexRingBufferMemSize) {
return RET_ERROR(SCE_GXM_ERROR_RESERVE_FAILED); // TODO: Does not actually return this on immediate context
}

context->state.vertex_ring_buffer_used = next_used;
context->state.vertex_last_reserve_status = SceGxmLastReserveStatus::Available;
}
if (context->state.fragment_last_reserve_status == SceGxmLastReserveStatus::Reserved) {
const auto fragment_program = context->state.fragment_program.get(host.mem);
const auto program = fragment_program->program.get(host.mem);

const size_t size = program->default_uniform_buffer_count * 4;
const size_t next_used = context->state.fragment_ring_buffer_used + size;
assert(next_used <= context->state.params.fragmentRingBufferMemSize);
if (next_used > context->state.params.fragmentRingBufferMemSize) {
return RET_ERROR(SCE_GXM_ERROR_RESERVE_FAILED); // TODO: Does not actually return this on immediate context
}

context->state.fragment_ring_buffer_used = next_used;
context->state.fragment_last_reserve_status = SceGxmLastReserveStatus::Available;
}

if (!renderer::sync_state(context->renderer, context->state, host.mem, host.gui.texture_cache)) {
return RET_ERROR(SCE_GXM_ERROR_DRIVER);
}
Expand Down Expand Up @@ -884,22 +913,12 @@ EXPORT(int, sceGxmRenderTargetGetDriverMemBlock) {
}

EXPORT(int, sceGxmReserveFragmentDefaultUniformBuffer, SceGxmContext *context, Ptr<void> *uniformBuffer) {
assert(context != nullptr);
assert(uniformBuffer != nullptr);

const auto fragment_program = context->state.fragment_program.get(host.mem);
const auto program = fragment_program->program.get(host.mem);

const size_t size = program->default_uniform_buffer_count * 4;
const size_t next_used = context->state.fragment_ring_buffer_used + size;
assert(next_used <= context->state.params.fragmentRingBufferMemSize);
if (next_used > context->state.params.fragmentRingBufferMemSize) {
return RET_ERROR(SCE_GXM_ERROR_OUT_OF_MEMORY);
}
if (!context || !uniformBuffer)
return SCE_GXM_ERROR_INVALID_POINTER;

*uniformBuffer = context->state.params.fragmentRingBufferMem.cast<uint8_t>() + static_cast<int32_t>(context->state.fragment_ring_buffer_used);
context->state.fragment_ring_buffer_used = next_used;

context->state.fragment_last_reserve_status == SceGxmLastReserveStatus::Reserved;
context->state.fragment_uniform_buffers[SCE_GXM_DEFAULT_UNIFORM_BUFFER_CONTAINER_INDEX] = *uniformBuffer;

return 0;
Expand All @@ -910,22 +929,12 @@ EXPORT(int, sceGxmRenderTargetGetHostMem) {
}

EXPORT(int, sceGxmReserveVertexDefaultUniformBuffer, SceGxmContext *context, Ptr<void> *uniformBuffer) {
assert(context != nullptr);
assert(uniformBuffer != nullptr);

const auto vertex_program = context->state.vertex_program.get(host.mem);
const auto program = vertex_program->program.get(host.mem);

const size_t size = program->default_uniform_buffer_count * 4;
const size_t next_used = context->state.vertex_ring_buffer_used + size;
assert(next_used <= context->state.params.vertexRingBufferMemSize);
if (next_used > context->state.params.vertexRingBufferMemSize) {
return RET_ERROR(SCE_GXM_ERROR_OUT_OF_MEMORY);
}
if (!context || !uniformBuffer)
return SCE_GXM_ERROR_INVALID_POINTER;

*uniformBuffer = context->state.params.vertexRingBufferMem.cast<uint8_t>() + static_cast<int32_t>(context->state.vertex_ring_buffer_used);
context->state.vertex_ring_buffer_used = next_used;

context->state.vertex_last_reserve_status == SceGxmLastReserveStatus::Reserved;
context->state.vertex_uniform_buffers[SCE_GXM_DEFAULT_UNIFORM_BUFFER_CONTAINER_INDEX] = *uniformBuffer;

return 0;
Expand Down

0 comments on commit 5b97fe1

Please sign in to comment.