Skip to content

Commit

Permalink
renderer/vulkan: Implement Memory Mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
Macdu authored and Zangetsu38 committed Feb 15, 2023
1 parent 644404f commit 762ee3b
Show file tree
Hide file tree
Showing 29 changed files with 900 additions and 309 deletions.
1 change: 1 addition & 0 deletions vita3k/features/include/features/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct FeatureState {
bool support_unknown_format = false;
bool support_rgb_attributes = true; ///< Do the GPU supports RGB (3 components) vertex attribute? If not (AMD GPU), some modifications must be applied to the renderer and the shader recompiler
bool use_mask_bit = false; ///< Is the mask bit (1 per sample) emulated ? It is only used in homebrews afaik
bool support_memory_mapping = false; ///< Is the host GPU memory directly mapped with gxm memory?

bool is_programmable_blending_supported() const {
return support_shader_interlock || support_texture_barrier || direct_fragcolor;
Expand Down
98 changes: 59 additions & 39 deletions vita3k/modules/SceGxm/SceGxm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2075,24 +2075,30 @@ static int gxmDrawElementGeneral(EmuEnvState &emuenv, const char *export_name, c
// Update vertex data. We should stores a copy of the data to pass it to GPU later, since another scene
// may start to overwrite stuff when this scene is being processed in our queue (in case of OpenGL).
size_t max_index = 0;
if (indexType == SCE_GXM_INDEX_FORMAT_U16) {
const uint16_t *const data = static_cast<const uint16_t *>(indexData);
max_index = *std::max_element(&data[0], &data[indexCount]);
} else {
const uint32_t *const data = static_cast<const uint32_t *>(indexData);
max_index = *std::max_element(&data[0], &data[indexCount]);
if (!emuenv.renderer->features.support_memory_mapping) {
// we don't need to get the vertex buffer size with memory mapping
if (indexType == SCE_GXM_INDEX_FORMAT_U16) {
const uint16_t *const data = static_cast<const uint16_t *>(indexData);
max_index = *std::max_element(&data[0], &data[indexCount]);
} else {
const uint32_t *const data = static_cast<const uint32_t *>(indexData);
max_index = *std::max_element(&data[0], &data[indexCount]);
}
}

size_t max_data_length[SCE_GXM_MAX_VERTEX_STREAMS] = {};
std::uint32_t stream_used = 0;
for (const SceGxmVertexAttribute &attribute : gxm_vertex_program.attributes) {
const SceGxmAttributeFormat attribute_format = static_cast<SceGxmAttributeFormat>(attribute.format);
const size_t attribute_size = gxm::attribute_format_size(attribute_format) * attribute.componentCount;
const SceGxmVertexStream &stream = gxm_vertex_program.streams[attribute.streamIndex];
const SceGxmIndexSource index_source = static_cast<SceGxmIndexSource>(stream.indexSource);
const size_t data_passed_length = gxm::is_stream_instancing(index_source) ? ((instanceCount - 1) * stream.stride) : (max_index * stream.stride);
const size_t data_length = attribute.offset + data_passed_length + attribute_size;
max_data_length[attribute.streamIndex] = std::max<size_t>(max_data_length[attribute.streamIndex], data_length);
if (!emuenv.renderer->features.support_memory_mapping) {
const SceGxmAttributeFormat attribute_format = static_cast<SceGxmAttributeFormat>(attribute.format);
const size_t attribute_size = gxm::attribute_format_size(attribute_format) * attribute.componentCount;
const SceGxmVertexStream &stream = gxm_vertex_program.streams[attribute.streamIndex];
const SceGxmIndexSource index_source = static_cast<SceGxmIndexSource>(stream.indexSource);
const size_t data_passed_length = gxm::is_stream_instancing(index_source) ? ((instanceCount - 1) * stream.stride) : (max_index * stream.stride);
const size_t data_length = attribute.offset + data_passed_length + attribute_size;
max_data_length[attribute.streamIndex] = std::max<size_t>(max_data_length[attribute.streamIndex], data_length);
}

stream_used |= (1 << attribute.streamIndex);
}

Expand Down Expand Up @@ -2175,13 +2181,16 @@ EXPORT(int, sceGxmDrawPrecomputed, SceGxmContext *context, SceGxmPrecomputedDraw

// Update vertex data. We should stores a copy of the data to pass it to GPU later, since another scene
// may start to overwrite stuff when this scene is being processed in our queue (in case of OpenGL).
size_t max_index = 0;
if (draw->index_format == SCE_GXM_INDEX_FORMAT_U16) {
const uint16_t *const data = draw->index_data.cast<const uint16_t>().get(emuenv.mem);
max_index = *std::max_element(&data[0], &data[draw->vertex_count]);
} else {
const uint32_t *const data = draw->index_data.cast<const uint32_t>().get(emuenv.mem);
max_index = *std::max_element(&data[0], &data[draw->vertex_count]);
uint32_t max_index = 0;
if (!emuenv.renderer->features.support_memory_mapping) {
// we don't need to get the vertex buffer size with memory mapping
if (draw->index_format == SCE_GXM_INDEX_FORMAT_U16) {
const uint16_t *const data = draw->index_data.cast<const uint16_t>().get(emuenv.mem);
max_index = *std::max_element(&data[0], &data[draw->vertex_count]);
} else {
const uint32_t *const data = draw->index_data.cast<const uint32_t>().get(emuenv.mem);
max_index = *std::max_element(&data[0], &data[draw->vertex_count]);
}
}

// set all textures that are used and mark them as dirty
Expand All @@ -2204,13 +2213,16 @@ EXPORT(int, sceGxmDrawPrecomputed, SceGxmContext *context, SceGxmPrecomputedDraw
size_t max_data_length[SCE_GXM_MAX_VERTEX_STREAMS] = {};
std::uint32_t stream_used = 0;
for (const SceGxmVertexAttribute &attribute : vertex_program->attributes) {
const SceGxmAttributeFormat attribute_format = static_cast<SceGxmAttributeFormat>(attribute.format);
const size_t attribute_size = gxm::attribute_format_size(attribute_format) * attribute.componentCount;
const SceGxmVertexStream &stream = vertex_program->streams[attribute.streamIndex];
const SceGxmIndexSource index_source = static_cast<SceGxmIndexSource>(stream.indexSource);
const size_t data_passed_length = gxm::is_stream_instancing(index_source) ? ((draw->instance_count - 1) * stream.stride) : (max_index * stream.stride);
const size_t data_length = attribute.offset + data_passed_length + attribute_size;
max_data_length[attribute.streamIndex] = std::max<size_t>(max_data_length[attribute.streamIndex], data_length);
if (!emuenv.renderer->features.support_memory_mapping) {
const SceGxmAttributeFormat attribute_format = static_cast<SceGxmAttributeFormat>(attribute.format);
const size_t attribute_size = gxm::attribute_format_size(attribute_format) * attribute.componentCount;
const SceGxmVertexStream &stream = vertex_program->streams[attribute.streamIndex];
const SceGxmIndexSource index_source = static_cast<SceGxmIndexSource>(stream.indexSource);
const size_t data_passed_length = gxm::is_stream_instancing(index_source) ? ((draw->instance_count - 1) * stream.stride) : (max_index * stream.stride);
const size_t data_length = attribute.offset + data_passed_length + attribute_size;
max_data_length[attribute.streamIndex] = std::max<size_t>(max_data_length[attribute.streamIndex], data_length);
}

stream_used |= (1 << attribute.streamIndex);
}

Expand Down Expand Up @@ -2281,18 +2293,10 @@ EXPORT(int, sceGxmEndScene, SceGxmContext *context, SceGxmNotification *vertexNo
return RET_ERROR(SCE_GXM_ERROR_WITHIN_SCENE);
}

// Add command to end the scene
renderer::sync_surface_data(*emuenv.renderer, context->renderer.get());
SceGxmNotification empty_notification = { Ptr<uint32_t>(0), 0 };

if (vertexNotification) {
renderer::add_command(context->renderer.get(), renderer::CommandOpcode::SignalNotification,
nullptr, *vertexNotification, true);
}

if (fragmentNotification) {
renderer::add_command(context->renderer.get(), renderer::CommandOpcode::SignalNotification,
nullptr, *fragmentNotification, false);
}
// Add command to end the scene
renderer::sync_surface_data(*emuenv.renderer, context->renderer.get(), vertexNotification ? *vertexNotification : empty_notification, fragmentNotification ? *fragmentNotification : empty_notification);

if (context->state.fragment_sync_object) {
SceGxmSyncObject *sync = context->state.fragment_sync_object.get(mem);
Expand Down Expand Up @@ -2594,8 +2598,21 @@ EXPORT(int, sceGxmMapMemory, Ptr<void> base, uint32_t size, uint32_t attribs) {
GxmState &gxm = emuenv.gxm;

auto ite = gxm.memory_mapped_regions.lower_bound(base.address());
if ((ite == gxm.memory_mapped_regions.end()) || (ite->first != base.address())) {
if (ite == gxm.memory_mapped_regions.end() || ite->first != base.address()) {
if (ite != gxm.memory_mapped_regions.end() && base.address() + size >= ite->first) {
LOG_ERROR("Overlapping mapped memory detected");

if (emuenv.renderer->features.support_memory_mapping) {
// overlapping memory mapping is not supported
return RET_ERROR(SCE_GXM_ERROR_INVALID_POINTER);
}
}
gxm.memory_mapped_regions.emplace(base.address(), MemoryMapInfo{ base.address(), size, attribs });

// little big planet maps regions of size 0
if (emuenv.renderer->features.support_memory_mapping && size > 0)
renderer::send_single_command(*emuenv.renderer, nullptr, renderer::CommandOpcode::MemoryMap, true, base, size);

return 0;
}

Expand Down Expand Up @@ -5216,6 +5233,9 @@ EXPORT(int, sceGxmUnmapMemory, Ptr<void> base) {
return RET_ERROR(SCE_GXM_ERROR_INVALID_POINTER);
}

if (emuenv.renderer->features.support_memory_mapping && ite->second.size > 0)
renderer::send_single_command(*emuenv.renderer, nullptr, renderer::CommandOpcode::MemoryUnmap, true, base);

emuenv.gxm.memory_mapped_regions.erase(ite);
return 0;
}
Expand Down
3 changes: 3 additions & 0 deletions vita3k/renderer/include/renderer/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ enum class CommandOpcode : std::uint8_t {
CreateContext,
CreateRenderTarget,

MemoryMap,
MemoryUnmap,

/**
* Do draw.
*/
Expand Down
2 changes: 2 additions & 0 deletions vita3k/renderer/include/renderer/driver_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ COMMAND(handle_create_context);
COMMAND(handle_destroy_context);
COMMAND(handle_create_render_target);
COMMAND(handle_destroy_render_target);
COMMAND(handle_memory_map);
COMMAND(handle_memory_unmap);

// Scene
COMMAND(handle_set_context);
Expand Down
2 changes: 1 addition & 1 deletion vita3k/renderer/include/renderer/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ void draw(State &state, Context *ctx, SceGxmPrimitiveType prim_type, SceGxmIndex
void transfer_copy(State &state, uint32_t colorKeyValue, uint32_t colorKeyMask, SceGxmTransferColorKeyMode colorKeyMode, const SceGxmTransferImage *images, SceGxmTransferType srcType, SceGxmTransferType destType);
void transfer_downscale(State &state, const SceGxmTransferImage *src, const SceGxmTransferImage *dest);
void transfer_fill(State &state, uint32_t fillColor, const SceGxmTransferImage *dest);
void sync_surface_data(State &state, Context *ctx);
void sync_surface_data(State &state, Context *ctx, const SceGxmNotification vertex_notification, const SceGxmNotification fragment_notification);

bool create_context(State &state, std::unique_ptr<Context> &context);
void destroy_context(State &state, std::unique_ptr<Context> &context);
Expand Down
6 changes: 6 additions & 0 deletions vita3k/renderer/include/renderer/gl/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ struct GLContext : public renderer::Context {
std::pair<std::uint8_t *, std::size_t> vertex_uniform_buffer_storage_ptr{ nullptr, 0 };
std::pair<std::uint8_t *, std::size_t> fragment_uniform_buffer_storage_ptr{ nullptr, 0 };

shader::RenderVertUniformBlock previous_vert_info;
shader::RenderFragUniformBlock previous_frag_info;

shader::RenderVertUniformBlock current_vert_render_info;
shader::RenderFragUniformBlock current_frag_render_info;

std::vector<size_t> self_sampling_indices;

explicit GLContext();
Expand Down
4 changes: 4 additions & 0 deletions vita3k/renderer/include/renderer/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ struct State {
virtual void set_fxaa(bool enable_fxaa) = 0;
virtual int get_max_anisotropic_filtering() = 0;
virtual void set_anisotropic_filtering(int anisotropic_filtering) = 0;
virtual bool map_memory(void *address, uint32_t size) {
return true;
}
virtual void unmap_memory(void *address) {}
virtual std::vector<std::string> get_gpu_list() {
return { "Automatic" };
}
Expand Down
7 changes: 1 addition & 6 deletions vita3k/renderer/include/renderer/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <bitset>
#include <map>
#include <string>
#include <thread>
#include <tuple>
#include <vector>

Expand Down Expand Up @@ -174,12 +175,6 @@ struct Context {
Sha256Hash last_draw_fragment_program_hash;
Sha256Hash last_draw_vertex_program_hash;

shader::RenderVertUniformBlock previous_vert_info;
shader::RenderFragUniformBlock previous_frag_info;

shader::RenderVertUniformBlock current_vert_render_info;
shader::RenderFragUniformBlock current_frag_render_info;

std::map<int, std::vector<uint8_t>> ubo_data;

shader::Hints shader_hints;
Expand Down
7 changes: 2 additions & 5 deletions vita3k/renderer/include/renderer/vulkan/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,22 @@
#include "state.h"

struct Config;
struct MemState;

namespace renderer::vulkan {

bool create(SDL_Window *window, std::unique_ptr<renderer::State> &state, const char *base_path);

bool create(VKState &state, std::unique_ptr<Context> &context);
bool create(VKState &state, std::unique_ptr<Context> &context, MemState &mem);
bool create(VKState &state, std::unique_ptr<RenderTarget> &rt, const SceGxmRenderTargetParams &params, const FeatureState &features);
void destroy(VKState &state, std::unique_ptr<RenderTarget> &rt);
bool create(std::unique_ptr<VertexProgram> &vp, VKState &state, const SceGxmProgram &program);
bool create(std::unique_ptr<FragmentProgram> &fp, VKState &state, const SceGxmProgram &program, const SceGxmBlendInfo *blend);
void create(SceGxmSyncObject *sync);
void destroy(SceGxmSyncObject *sync);

void draw(VKContext &context, SceGxmPrimitiveType type, SceGxmIndexFormat format,
void *indices, size_t count, uint32_t instance_count, MemState &mem, const Config &config);

void new_frame(VKContext &context);
void update_sync_target(SceGxmSyncObject *sync, VKRenderTarget *target);
void update_sync_signal(SceGxmSyncObject *sync);

void set_context(VKContext &context, const MemState &mem, VKRenderTarget *rt, const FeatureState &features);
void set_uniform_buffer(VKContext &context, const ShaderProgram *program, const bool vertex_shader, const int block_num, const int size, const uint8_t *data);
Expand Down
12 changes: 12 additions & 0 deletions vita3k/renderer/include/renderer/vulkan/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ struct VKState : public renderer::State {
// Transfer pool has transient bit set.
vk::CommandPool transfer_command_pool;

// only used when memory mapping is enabled
std::map<uint64_t, MappedMemory, std::greater<uint64_t>> mapped_memories;

vkutil::Image default_image;
vkutil::Buffer default_buffer;

VKState(int gpu_idx);

bool init(const char *base_path, const bool hashless_texture_cache) override;
Expand All @@ -73,6 +79,12 @@ struct VKState : public renderer::State {
void set_fxaa(bool enable_fxaa) override;
int get_max_anisotropic_filtering() override;
void set_anisotropic_filtering(int anisotropic_filtering) override;
bool map_memory(void *address, uint32_t size) override;
void unmap_memory(void *address) override;
// return the matching buffer and offset for the memory location
std::tuple<vk::Buffer, uint32_t> get_matching_mapping(const void *address);
// return the GPU buffer device address matching this one
uint64_t get_matching_device_address(const void *address);
std::vector<std::string> get_gpu_list() override;

void precompile_shader(const ShadersHash &hash) override;
Expand Down

0 comments on commit 762ee3b

Please sign in to comment.