diff --git a/vita3k/config/include/config/config.h b/vita3k/config/include/config/config.h index f51b864579..4c926d77b1 100644 --- a/vita3k/config/include/config/config.h +++ b/vita3k/config/include/config/config.h @@ -67,7 +67,7 @@ enum PerfomanceOverleyPosition { code(std::string, "backend-renderer", "OpenGL", backend_renderer) \ code(int, "gpu-idx", 0, gpu_idx) \ code(bool, "high-accuracy", true, high_accuracy) \ - code(int, "resolution-multiplier", 1, resolution_multiplier) \ + code(float, "resolution-multiplier", 1.0f, resolution_multiplier) \ code(bool, "disable-surface-sync", true, disable_surface_sync) \ code(std::string, "screen-filter", "Bilinear", screen_filter) \ code(bool, "v-sync", true, v_sync) \ diff --git a/vita3k/config/include/config/state.h b/vita3k/config/include/config/state.h index e8a3241b56..5d6a5254cd 100644 --- a/vita3k/config/include/config/state.h +++ b/vita3k/config/include/config/state.h @@ -130,7 +130,7 @@ struct Config : YamlLoader { bool ngs_enable = true; bool pstv_mode = false; bool high_accuracy = false; - int resolution_multiplier = 1; + float resolution_multiplier = 1.0f; bool disable_surface_sync = false; std::string screen_filter = "Bilinear"; bool v_sync = true; diff --git a/vita3k/gui/src/settings_dialog.cpp b/vita3k/gui/src/settings_dialog.cpp index 881a15d91d..d4de7a29b3 100644 --- a/vita3k/gui/src/settings_dialog.cpp +++ b/vita3k/gui/src/settings_dialog.cpp @@ -173,7 +173,7 @@ static bool get_custom_config(GuiState &gui, EmuEnvState &emuenv, const std::str if (!config_child.child("gpu").empty()) { const auto gpu_child = config_child.child("gpu"); config.high_accuracy = gpu_child.attribute("high-accuracy").as_bool(); - config.resolution_multiplier = gpu_child.attribute("resolution-multiplier").as_int(); + config.resolution_multiplier = gpu_child.attribute("resolution-multiplier").as_float(); config.disable_surface_sync = gpu_child.attribute("disable-surface-sync").as_bool(); config.screen_filter = gpu_child.attribute("screen-filter").as_string(); config.v_sync = gpu_child.attribute("v-sync").as_bool(); @@ -629,7 +629,13 @@ void draw_settings_dialog(GuiState &gui, EmuEnvState &emuenv) { ImGui::SetTooltip("%s", lang.gpu["v_sync_description"].c_str()); ImGui::SameLine(); } - if (!is_vulkan || emuenv.renderer->features.support_memory_mapping) { + + const bool has_integer_multiplier = static_cast(config.resolution_multiplier * 4.0f) % 4 == 0; + // OpenGL does not support surface sync with a non-integer resolution multiplier + if (!is_vulkan && has_integer_multiplier) + config.disable_surface_sync = true; + + if ((!is_vulkan && has_integer_multiplier) || emuenv.renderer->features.support_memory_mapping) { // surface sync is supported on vulkan only when memory mapping is enabled ImGui::Checkbox(lang.gpu["disable_surface_sync"].c_str(), &config.disable_surface_sync); if (ImGui::IsItemHovered()) @@ -679,38 +685,40 @@ void draw_settings_dialog(GuiState &gui, EmuEnvState &emuenv) { ImGui::TextColored(GUI_COLOR_TEXT_TITLE, "%s", lang.gpu["internal_resolution_upscaling"].c_str()); ImGui::Spacing(); ImGui::PushID("Res scal"); - if (config.resolution_multiplier == 1) + if (config.resolution_multiplier == 0.5f) ImGui::BeginDisabled(); if (ImGui::Button("<", ImVec2(20.f * SCALE.x, 0))) - --config.resolution_multiplier; - if (config.resolution_multiplier == 1) + config.resolution_multiplier -= 0.25f; + if (config.resolution_multiplier == 0.5f) ImGui::EndDisabled(); ImGui::SameLine(0, 5.f * SCALE.x); ImGui::PushItemWidth(-100.f * SCALE.x); - if (ImGui::SliderInt("##res_scal", &config.resolution_multiplier, 1, 8, fmt::format("{}x", config.resolution_multiplier).c_str(), ImGuiSliderFlags_None)) { - if (config.resolution_multiplier > 1 && !is_vulkan) + int slider_position = static_cast(config.resolution_multiplier * 4); + if (ImGui::SliderInt("##res_scal", &slider_position, 2, 32, fmt::format("{}x", config.resolution_multiplier).c_str(), ImGuiSliderFlags_None)) { + config.resolution_multiplier = static_cast(slider_position) / 4.0f; + if (config.resolution_multiplier != 1.0f && !is_vulkan) config.disable_surface_sync = true; } ImGui::PopItemWidth(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", lang.gpu["internal_resolution_upscaling_description"].c_str()); ImGui::SameLine(0, 5 * SCALE.x); - if (config.resolution_multiplier == 8) + if (config.resolution_multiplier == 8.0f) ImGui::BeginDisabled(); if (ImGui::Button(">", ImVec2(20.f * SCALE.x, 0))) - ++config.resolution_multiplier; - if (config.resolution_multiplier == 8) + config.resolution_multiplier += 0.25f; + if (config.resolution_multiplier == 8.0f) ImGui::EndDisabled(); ImGui::SameLine(); - if ((config.resolution_multiplier == 1) && !config.disable_surface_sync) + if ((config.resolution_multiplier == 1.0f) && !config.disable_surface_sync) ImGui::BeginDisabled(); if (ImGui::Button(lang.gpu["reset"].c_str(), ImVec2(60.f * SCALE.x, 0))) - config.resolution_multiplier = 1; + config.resolution_multiplier = 1.0f; - if ((config.resolution_multiplier == 1) && !config.disable_surface_sync) + if ((config.resolution_multiplier == 1.0f) && !config.disable_surface_sync) ImGui::EndDisabled(); ImGui::Spacing(); - const auto res_scal = fmt::format("{}x{}", 960 * config.resolution_multiplier, 544 * config.resolution_multiplier); + const auto res_scal = fmt::format("{}x{}", static_cast(960 * config.resolution_multiplier), static_cast(544 * config.resolution_multiplier)); ImGui::SetCursorPosX((ImGui::GetWindowWidth() / 2.f) - (ImGui::CalcTextSize(res_scal.c_str()).x / 2.f) - (35.f * SCALE.x)); ImGui::Text("%s", res_scal.c_str()); ImGui::PopID(); diff --git a/vita3k/renderer/include/renderer/gl/surface_cache.h b/vita3k/renderer/include/renderer/gl/surface_cache.h index f892a7a875..fa5c6d646e 100644 --- a/vita3k/renderer/include/renderer/gl/surface_cache.h +++ b/vita3k/renderer/include/renderer/gl/surface_cache.h @@ -127,8 +127,8 @@ class GLSurfaceCache { target = new_target; } - GLuint sourcing_color_surface_for_presentation(Ptr address, uint32_t width, uint32_t height, const std::uint32_t pitch, float *uvs, const int res_multiplier, SceFVector2 &texture_size); - std::vector dump_frame(Ptr address, uint32_t width, uint32_t height, uint32_t pitch, int res_multiplier, bool support_get_texture_sub_image); + GLuint sourcing_color_surface_for_presentation(Ptr address, uint32_t width, uint32_t height, const std::uint32_t pitch, float *uvs, const float res_multiplier, SceFVector2 &texture_size); + std::vector dump_frame(Ptr address, uint32_t width, uint32_t height, uint32_t pitch, float res_multiplier, bool support_get_texture_sub_image); }; } // namespace gl } // namespace renderer diff --git a/vita3k/renderer/include/renderer/state.h b/vita3k/renderer/include/renderer/state.h index cce86cb992..1cbea15bb4 100644 --- a/vita3k/renderer/include/renderer/state.h +++ b/vita3k/renderer/include/renderer/state.h @@ -54,7 +54,7 @@ struct State { Backend current_backend; FeatureState features; - int res_multiplier; + float res_multiplier; bool disable_surface_sync; bool stretch_the_display_area; diff --git a/vita3k/renderer/src/creation.cpp b/vita3k/renderer/src/creation.cpp index 51a5007e1e..596610a098 100644 --- a/vita3k/renderer/src/creation.cpp +++ b/vita3k/renderer/src/creation.cpp @@ -123,8 +123,8 @@ COMMAND(handle_create_render_target) { uint16_t nb_macroblocks_y = (params->flags >> 12) & 0b111; // the width and height should be multiple of 128 - (*render_target)->macroblock_width = (params->width / nb_macroblocks_x) * renderer.res_multiplier; - (*render_target)->macroblock_height = (params->height / nb_macroblocks_y) * renderer.res_multiplier; + (*render_target)->macroblock_width = static_cast((params->width / nb_macroblocks_x) * renderer.res_multiplier); + (*render_target)->macroblock_height = static_cast((params->height / nb_macroblocks_y) * renderer.res_multiplier); } complete_command(renderer, helper, result); diff --git a/vita3k/renderer/src/gl/draw.cpp b/vita3k/renderer/src/gl/draw.cpp index 644d466038..3011d5d40f 100644 --- a/vita3k/renderer/src/gl/draw.cpp +++ b/vita3k/renderer/src/gl/draw.cpp @@ -155,7 +155,7 @@ void draw(GLState &renderer, GLContext &context, const FeatureState &features, S } frag_ublock.writing_mask = context.record.writing_mask; frag_ublock.use_raw_image = static_cast(use_raw_image); - frag_ublock.res_multiplier = static_cast(renderer.res_multiplier); + frag_ublock.res_multiplier = renderer.res_multiplier; const bool has_msaa = context.render_target->multisample_mode; const bool has_downscale = context.record.color_surface.downscale; if (has_msaa && !has_downscale) diff --git a/vita3k/renderer/src/gl/renderer.cpp b/vita3k/renderer/src/gl/renderer.cpp index 551080f9b9..b4da15dd7f 100644 --- a/vita3k/renderer/src/gl/renderer.cpp +++ b/vita3k/renderer/src/gl/renderer.cpp @@ -300,8 +300,8 @@ bool create(GLState &state, std::unique_ptr &rt, const SceGxmRende return false; } - render_target->width = params.width * state.res_multiplier; - render_target->height = params.height * state.res_multiplier; + render_target->width = static_cast(params.width * state.res_multiplier); + render_target->height = static_cast(params.height * state.res_multiplier); render_target->attachments.init(reinterpret_cast(glGenTextures), reinterpret_cast(glDeleteTextures)); @@ -317,7 +317,7 @@ bool create(GLState &state, std::unique_ptr &rt, const SceGxmRende glBindTexture(GL_TEXTURE_2D, render_target->masktexture[0]); // we need to make the masktexture format immutable, otherwise image load operations // won't work on mesa drivers - glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, params.width * state.res_multiplier, params.height * state.res_multiplier); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, render_target->width, render_target->height); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindFramebuffer(GL_FRAMEBUFFER, render_target->maskbuffer[0]); @@ -443,7 +443,7 @@ static std::map> GXM_COLOR_FORMAT_T static bool format_need_temp_storage(const GLState &state, SceGxmColorSurface &surface, std::vector &storage, const std::uint32_t width, const std::uint32_t height) { size_t needed_pixels; - if (state.res_multiplier == 1) { + if (state.res_multiplier == 1.0f) { needed_pixels = surface.strideInPixels * height; } else { // width and height is already upscaled @@ -455,7 +455,7 @@ static bool format_need_temp_storage(const GLState &state, SceGxmColorSurface &s return true; } - if (state.res_multiplier > 1) { + if (state.res_multiplier > 1.0f) { storage.resize(needed_pixels * gxm::bits_per_pixel(gxm::get_base_format(surface.colorFormat)) >> 3); return true; } @@ -471,7 +471,7 @@ static void post_process_pixels_data(GLState &renderer, std::uint32_t *pixels, s const bool is_U8U8U8_RGBA = surface.colorFormat == SCE_GXM_COLOR_FORMAT_U8U8U8U8_RGBA; const bool is_SE5M9M9M9 = (surface.colorFormat == SCE_GXM_COLOR_FORMAT_SE5M9M9M9_RGB) || (surface.colorFormat == SCE_GXM_COLOR_FORMAT_SE5M9M9M9_BGR); - const int multiplier = renderer.res_multiplier; + const int multiplier = static_cast(renderer.res_multiplier); if (multiplier > 1 || is_U8U8U8_RGBA || is_SE5M9M9M9) { // TODO: do this on the GPU instead (using texture blitting?) const int bytes_per_output_pixel = (gxm::bits_per_pixel(gxm::get_base_format(surface.colorFormat)) + 7) >> 3; @@ -608,11 +608,12 @@ void get_surface_data(GLState &renderer, GLContext &context, uint32_t *pixels, S uint32_t width = surface.width; uint32_t height = surface.height; - if (renderer.res_multiplier == 1) { + const int res_multiplier = static_cast(renderer.res_multiplier); + if (res_multiplier == 1) { glPixelStorei(GL_PACK_ROW_LENGTH, static_cast(surface.strideInPixels)); } else { - width *= renderer.res_multiplier; - height *= renderer.res_multiplier; + width *= res_multiplier; + height *= res_multiplier; glPixelStorei(GL_PACK_ROW_LENGTH, static_cast(width)); } @@ -723,8 +724,8 @@ std::vector GLState::dump_frame(DisplayState &display, uint32_t &width frame = display.next_rendered_frame; } - width = frame.image_size.x * res_multiplier; - height = frame.image_size.y * res_multiplier; + width = static_cast(frame.image_size.x * res_multiplier); + height = static_cast(frame.image_size.y * res_multiplier); return surface_cache.dump_frame(frame.base, width, height, frame.pitch, res_multiplier, features.support_get_texture_sub_image); } diff --git a/vita3k/renderer/src/gl/surface_cache.cpp b/vita3k/renderer/src/gl/surface_cache.cpp index 5c7008a320..37e9441f2d 100644 --- a/vita3k/renderer/src/gl/surface_cache.cpp +++ b/vita3k/renderer/src/gl/surface_cache.cpp @@ -66,8 +66,8 @@ GLuint GLSurfaceCache::retrieve_color_surface_texture_handle(const State &state, const uint32_t original_width = width; const uint32_t original_height = height; - width *= state.res_multiplier; - height *= state.res_multiplier; + width = static_cast(width * state.res_multiplier); + height = static_cast(height * state.res_multiplier); // Of course, this works under the assumption that range must be unique :D auto ite = color_surface_textures.lower_bound(key); @@ -271,8 +271,8 @@ GLuint GLSurfaceCache::retrieve_color_surface_texture_handle(const State &state, if (castable) { const std::size_t data_delta = address.address() - ite->first; - std::size_t start_sourced_line = (data_delta / bytes_per_stride) * state.res_multiplier; - std::size_t start_x = (data_delta % bytes_per_stride) / color::bytes_per_pixel(base_format) * state.res_multiplier; + std::size_t start_sourced_line = static_cast((data_delta / bytes_per_stride) * state.res_multiplier); + std::size_t start_x = static_cast((data_delta % bytes_per_stride) / color::bytes_per_pixel(base_format) * state.res_multiplier); if (static_cast(start_sourced_line + height) > info.height) { LOG_ERROR("Trying to present non-existen segment in cached color surface!"); @@ -521,16 +521,15 @@ GLuint GLSurfaceCache::retrieve_depth_stencil_texture_handle(const State &state, return 0; } - force_width *= state.res_multiplier; - force_height *= state.res_multiplier; - - if (force_width < 0) { + if (force_width > 0) + force_width = static_cast(force_width * state.res_multiplier); + else force_width = target->width; - } - if (force_height < 0) { + if (force_height > 0) + force_height = static_cast(force_height * state.res_multiplier); + else force_height = target->height; - } const bool is_stencil_only = surface.depth_data.address() == 0; std::size_t found_index = static_cast(-1); @@ -713,14 +712,14 @@ GLuint GLSurfaceCache::retrieve_framebuffer_handle(const State &state, const Mem return fb[0]; } -GLuint GLSurfaceCache::sourcing_color_surface_for_presentation(Ptr address, uint32_t width, uint32_t height, const std::uint32_t pitch, float *uvs, const int res_multiplier, SceFVector2 &texture_size) { +GLuint GLSurfaceCache::sourcing_color_surface_for_presentation(Ptr address, uint32_t width, uint32_t height, const std::uint32_t pitch, float *uvs, const float res_multiplier, SceFVector2 &texture_size) { auto ite = color_surface_textures.lower_bound(address.address()); if (ite == color_surface_textures.end()) { return 0; } - width *= res_multiplier; - height *= res_multiplier; + width = static_cast(width * res_multiplier); + height = static_cast(height * res_multiplier); const GLColorSurfaceCacheInfo &info = *ite->second; @@ -729,7 +728,7 @@ GLuint GLSurfaceCache::sourcing_color_surface_for_presentation(Ptr a const std::size_t data_delta = address.address() - ite->first; std::uint32_t limited_height = height; if ((data_delta % (pitch * 4)) == 0) { - std::uint32_t start_sourced_line = (data_delta / (pitch * 4)) * res_multiplier; + std::uint32_t start_sourced_line = static_cast((data_delta / (pitch * 4)) * res_multiplier); if ((start_sourced_line + height) > info.height) { // Sometimes the surface is just missing a little bit of lines if (start_sourced_line < info.height) { @@ -758,7 +757,7 @@ GLuint GLSurfaceCache::sourcing_color_surface_for_presentation(Ptr a return 0; } -std::vector GLSurfaceCache::dump_frame(Ptr address, uint32_t width, uint32_t height, uint32_t pitch, int res_multiplier, bool support_get_texture_sub_image) { +std::vector GLSurfaceCache::dump_frame(Ptr address, uint32_t width, uint32_t height, uint32_t pitch, float res_multiplier, bool support_get_texture_sub_image) { auto ite = color_surface_textures.lower_bound(address.address()); if (ite == color_surface_textures.end() || ite->second->pixel_stride != pitch) { return {}; @@ -771,7 +770,7 @@ std::vector GLSurfaceCache::dump_frame(Ptr address, uint32 if (info.pixel_stride != pitch || data_delta % pitch_byte != 0) return {}; - const uint32_t line_delta = (data_delta / pitch_byte) * res_multiplier; + const uint32_t line_delta = static_cast((data_delta / pitch_byte) * res_multiplier); if (line_delta >= info.height) return {}; diff --git a/vita3k/renderer/src/gl/sync_state.cpp b/vita3k/renderer/src/gl/sync_state.cpp index e919156b15..87d69735cd 100644 --- a/vita3k/renderer/src/gl/sync_state.cpp +++ b/vita3k/renderer/src/gl/sync_state.cpp @@ -120,7 +120,8 @@ void sync_viewport_flat(const GLState &state, GLContext &context) { const GLsizei display_w = context.record.color_surface.width; const GLsizei display_h = context.record.color_surface.height; - glViewport(0, (context.current_framebuffer_height - display_h) * state.res_multiplier, display_w * state.res_multiplier, display_h * state.res_multiplier); + glViewport(0, static_cast((context.current_framebuffer_height - display_h) * state.res_multiplier), + static_cast(display_w * state.res_multiplier), static_cast(display_h * state.res_multiplier)); glDepthRange(0, 1); } @@ -161,7 +162,8 @@ void sync_clipping(const GLState &state, GLContext &context) { break; case SCE_GXM_REGION_CLIP_OUTSIDE: glEnable(GL_SCISSOR_TEST); - glScissor(scissor_x * state.res_multiplier, scissor_y * state.res_multiplier, scissor_w * state.res_multiplier, scissor_h * state.res_multiplier); + glScissor(static_cast(scissor_x * state.res_multiplier), static_cast(scissor_y * state.res_multiplier), + static_cast(scissor_w * state.res_multiplier), static_cast(scissor_h * state.res_multiplier)); break; case SCE_GXM_REGION_CLIP_INSIDE: // TODO: Implement SCE_GXM_REGION_CLIP_INSIDE diff --git a/vita3k/renderer/src/scene.cpp b/vita3k/renderer/src/scene.cpp index 08096060a7..7e6835f78b 100644 --- a/vita3k/renderer/src/scene.cpp +++ b/vita3k/renderer/src/scene.cpp @@ -132,6 +132,10 @@ COMMAND(handle_sync_surface_data) { } } + // additional check to make sure we never try to perform surface sync on OpenGL with a non-integer resolution multiplier + if (renderer.current_backend == Backend::OpenGL && static_cast(renderer.res_multiplier * 4.0f) % 4 != 0) + renderer.disable_surface_sync = true; + if (renderer.disable_surface_sync || renderer.current_backend == Backend::Vulkan) { if (helper.cmd->status) { complete_command(renderer, helper, 0); diff --git a/vita3k/renderer/src/texture/replacement.cpp b/vita3k/renderer/src/texture/replacement.cpp index 19c93720df..13014f3ae6 100644 --- a/vita3k/renderer/src/texture/replacement.cpp +++ b/vita3k/renderer/src/texture/replacement.cpp @@ -370,7 +370,7 @@ void TextureCache::export_texture_impl(SceGxmTextureBaseFormat base_format, uint } } else { // alpha is already linear - for (uint32_t i = 0; i < nb_pixels * 4; i++) { + for (uint32_t i = 0; i < nb_pixels; i++) { pixels[i * 4 + 0] = convert_to_linear(pixels[i * 4 + 0]); pixels[i * 4 + 1] = convert_to_linear(pixels[i * 4 + 1]); pixels[i * 4 + 2] = convert_to_linear(pixels[i * 4 + 2]); diff --git a/vita3k/renderer/src/vulkan/creation.cpp b/vita3k/renderer/src/vulkan/creation.cpp index 644ca3ae14..586a32d420 100644 --- a/vita3k/renderer/src/vulkan/creation.cpp +++ b/vita3k/renderer/src/vulkan/creation.cpp @@ -86,7 +86,7 @@ VKContext::VKContext(VKState &state, MemState &mem) }; scissor = vk::Rect2D{ .offset = { 0, 0 }, - .extent = { 960U * state.res_multiplier, 544U * state.res_multiplier } + .extent = { static_cast(960 * state.res_multiplier), static_cast(544U * state.res_multiplier) } }; // allocate descriptor pools @@ -153,10 +153,10 @@ VKContext::VKContext(VKState &state, MemState &mem) } VKRenderTarget::VKRenderTarget(VKState &state, const SceGxmRenderTargetParams ¶ms) - : color(params.width * state.res_multiplier, params.height * state.res_multiplier, vk::Format::eR8G8B8A8Unorm) - , depthstencil(params.width * state.res_multiplier, params.height * state.res_multiplier, vk::Format::eD32SfloatS8Uint) { - width = params.width * state.res_multiplier; - height = params.height * state.res_multiplier; + : color(static_cast(params.width * state.res_multiplier), static_cast(params.height * state.res_multiplier), vk::Format::eR8G8B8A8Unorm) + , depthstencil(static_cast(params.width * state.res_multiplier), static_cast(params.height * state.res_multiplier), vk::Format::eD32SfloatS8Uint) { + width = static_cast(params.width * state.res_multiplier); + height = static_cast(params.height * state.res_multiplier); vk::ImageUsageFlags color_usage = vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eInputAttachment; if (state.features.support_shader_interlock) diff --git a/vita3k/renderer/src/vulkan/renderer.cpp b/vita3k/renderer/src/vulkan/renderer.cpp index fe985b3760..3c23a55204 100644 --- a/vita3k/renderer/src/vulkan/renderer.cpp +++ b/vita3k/renderer/src/vulkan/renderer.cpp @@ -686,8 +686,8 @@ void VKState::render_frame(const SceFVector2 &viewport_pos, const SceFVector2 &v // Check if the surface exists Viewport viewport; - viewport.width = frame.image_size.x * res_multiplier; - viewport.height = frame.image_size.y * res_multiplier; + viewport.width = static_cast(frame.image_size.x * res_multiplier); + viewport.height = static_cast(frame.image_size.y * res_multiplier); vk::ImageLayout layout = vk::ImageLayout::eGeneral; vk::ImageView surface_handle = surface_cache.sourcing_color_surface_for_presentation( @@ -755,8 +755,8 @@ std::vector VKState::dump_frame(DisplayState &display, uint32_t &width frame = display.next_rendered_frame; } - width = frame.image_size.x * res_multiplier; - height = frame.image_size.y * res_multiplier; + width = static_cast(frame.image_size.x * res_multiplier); + height = static_cast(frame.image_size.y * res_multiplier); return surface_cache.dump_frame(frame.base, width, height, frame.pitch); } diff --git a/vita3k/renderer/src/vulkan/scene.cpp b/vita3k/renderer/src/vulkan/scene.cpp index 96dd7d7a7c..a9d68a5cac 100644 --- a/vita3k/renderer/src/vulkan/scene.cpp +++ b/vita3k/renderer/src/vulkan/scene.cpp @@ -419,8 +419,8 @@ void draw(VKContext &context, SceGxmPrimitiveType type, SceGxmIndexFormat format vert_ublock.viewport_flag = (context.record.viewport_flat) ? 0.0f : 1.0f; vert_ublock.z_offset = context.record.z_offset; vert_ublock.z_scale = context.record.z_scale; - vert_ublock.screen_width = static_cast(context.render_target->width / context.state.res_multiplier); - vert_ublock.screen_height = static_cast(context.render_target->height / context.state.res_multiplier); + vert_ublock.screen_width = context.render_target->width / context.state.res_multiplier; + vert_ublock.screen_height = context.render_target->height / context.state.res_multiplier; if (context.curr_vert_ublock.changed || memcmp(&context.prev_vert_ublock, &vert_ublock, sizeof(vert_ublock)) != 0) { // TODO: this intermediate step can be avoided @@ -431,7 +431,7 @@ void draw(VKContext &context, SceGxmPrimitiveType type, SceGxmIndexFormat format auto &frag_ublock = context.curr_frag_ublock.base_block; frag_ublock.writing_mask = context.record.writing_mask; - frag_ublock.res_multiplier = static_cast(context.state.res_multiplier); + frag_ublock.res_multiplier = context.state.res_multiplier; const bool has_msaa = context.render_target->multisample_mode; const bool has_downscale = context.record.color_surface.downscale; if (has_msaa && !has_downscale) diff --git a/vita3k/renderer/src/vulkan/surface_cache.cpp b/vita3k/renderer/src/vulkan/surface_cache.cpp index 0c51cbf8fa..5f752386da 100644 --- a/vita3k/renderer/src/vulkan/surface_cache.cpp +++ b/vita3k/renderer/src/vulkan/surface_cache.cpp @@ -124,8 +124,8 @@ SurfaceRetrieveResult VKSurfaceCache::retrieve_color_surface_for_framebuffer(Mem const uint32_t original_width = color->width; const uint32_t original_height = color->height; - uint32_t width = original_width * state.res_multiplier; - uint32_t height = original_height * state.res_multiplier; + uint32_t width = static_cast(original_width * state.res_multiplier); + uint32_t height = static_cast(original_height * state.res_multiplier); bool overlap = true; @@ -312,8 +312,8 @@ std::optional VKSurfaceCache::retrieve_color_surface_as_tex const uint32_t original_width = gxm::get_width(texture); const uint32_t original_height = gxm::get_height(texture); - const uint32_t width = original_width * state.res_multiplier; - const uint32_t height = original_height * state.res_multiplier; + const uint32_t width = static_cast(original_width * state.res_multiplier); + const uint32_t height = static_cast(original_height * state.res_multiplier); bool overlap = true; // Of course, this works under the assumption that range must be unique :D @@ -401,8 +401,8 @@ std::optional VKSurfaceCache::retrieve_color_surface_as_tex // TODO: this is true only for linear textures (and also kind of for tiled textures) (and in this case start_x = 0), // for swizzled textures this is different const uint32_t data_delta = address - ite->first; - uint32_t start_sourced_line = (data_delta / stride_bytes) * state.res_multiplier; - uint32_t start_x = (data_delta % stride_bytes) / bytes_per_pixel_requested * state.res_multiplier; + uint32_t start_sourced_line = static_cast((data_delta / stride_bytes) * state.res_multiplier); + uint32_t start_x = static_cast((data_delta % stride_bytes) / bytes_per_pixel_requested * state.res_multiplier); if (static_cast(start_sourced_line + height) > info.height) LOG_WARN_ONCE("Trying to use texture partially in the surface cache"); @@ -534,7 +534,7 @@ std::optional VKSurfaceCache::retrieve_color_surface_as_tex } else { LOG_INFO_ONCE("Game is doing typeless copies"); // We must use a transition buffer - vk::DeviceSize buffer_size = stride_bytes * state.res_multiplier * height + start_x * bytes_per_pixel_requested; + vk::DeviceSize buffer_size = stride_bytes * static_cast(state.res_multiplier * align(height, 4)) + start_x * bytes_per_pixel_requested; if (!casted->transition_buffer.buffer || casted->transition_buffer.size < buffer_size) { // create or re-create the buffer state.frame().destroy_queue.add_buffer(casted->transition_buffer); @@ -543,7 +543,7 @@ std::optional VKSurfaceCache::retrieve_color_surface_as_tex } // copy the image to the buffer - const uint32_t src_pixel_stride = (info.stride_bytes / bytes_per_pixel_in_store) * state.res_multiplier; + const uint32_t src_pixel_stride = static_cast((info.stride_bytes / bytes_per_pixel_in_store) * state.res_multiplier); vk::BufferImageCopy copy_image_buffer{ .bufferOffset = 0, .bufferRowLength = src_pixel_stride, @@ -608,8 +608,8 @@ std::optional VKSurfaceCache::retrieve_color_surface_as_tex SurfaceRetrieveResult VKSurfaceCache::retrieve_depth_stencil_for_framebuffer(SceGxmDepthStencilSurface *depth_stencil, const uint32_t width, const uint32_t height) { // when writing we use the render target size which is already upscaled - int32_t memory_width = width / state.res_multiplier; - int32_t memory_height = height / state.res_multiplier; + int32_t memory_width = static_cast(width / state.res_multiplier); + int32_t memory_height = static_cast(height / state.res_multiplier); const SurfaceTiling tiling = (depth_stencil->get_type() == SCE_GXM_DEPTH_STENCIL_SURFACE_LINEAR) ? SurfaceTiling::Linear : SurfaceTiling::Tiled; @@ -747,8 +747,8 @@ std::optional VKSurfaceCache::retrieve_depth_stencil_as_tex return std::nullopt; // take upscaling into account - uint32_t width = memory_width * state.res_multiplier; - uint32_t height = memory_height * state.res_multiplier; + uint32_t width = static_cast(memory_width * state.res_multiplier); + uint32_t height = static_cast(memory_height * state.res_multiplier); const uint32_t address = texture.data_addr << 2; DepthStencilSurfaceCacheInfo *found_info = nullptr; @@ -993,8 +993,8 @@ ColorSurfaceCacheInfo *VKSurfaceCache::perform_surface_sync() { is_swizzle_identity = true; } - if (state.res_multiplier > 1) { - // downscale the image using a blit command first + if (state.res_multiplier != 1.0f) { + // scale bacl the image using a blit command first if (!last_written_surface->blit_image) last_written_surface->blit_image = std::make_unique(); @@ -1177,7 +1177,7 @@ vk::ImageView VKSurfaceCache::sourcing_color_surface_for_presentation(Ptrfirst; uint32_t limited_height = viewport.height; if ((data_delta % (pitch * 4)) == 0) { - uint32_t start_sourced_line = (data_delta / (pitch * 4)) * state.res_multiplier; + uint32_t start_sourced_line = static_cast((data_delta / (pitch * 4)) * state.res_multiplier); if ((start_sourced_line + viewport.height) > info.height) { // Sometimes the surface is just missing a little bit of lines if (start_sourced_line < info.height) { @@ -1234,7 +1234,7 @@ std::vector VKSurfaceCache::dump_frame(Ptr address, uint32 if (info.stride_bytes != pitch_byte || data_delta % pitch_byte != 0) return {}; - const uint32_t line_delta = (data_delta / pitch_byte) * state.res_multiplier; + const uint32_t line_delta = static_cast((data_delta / pitch_byte) * state.res_multiplier); if (line_delta >= info.height) return {}; diff --git a/vita3k/renderer/src/vulkan/sync_state.cpp b/vita3k/renderer/src/vulkan/sync_state.cpp index 20876b24b3..07a02a9a9a 100644 --- a/vita3k/renderer/src/vulkan/sync_state.cpp +++ b/vita3k/renderer/src/vulkan/sync_state.cpp @@ -30,7 +30,7 @@ void sync_clipping(VKContext &context) { if (!context.render_target) return; - const int res_multiplier = context.state.res_multiplier; + const float res_multiplier = context.state.res_multiplier; const int scissor_x = context.record.region_clip_min.x; const int scissor_y = context.record.region_clip_min.y; @@ -48,8 +48,8 @@ void sync_clipping(VKContext &context) { break; case SCE_GXM_REGION_CLIP_OUTSIDE: context.scissor = vk::Rect2D{ - { scissor_x * res_multiplier, scissor_y * res_multiplier }, - { scissor_w * res_multiplier, scissor_h * res_multiplier } + { static_cast(scissor_x * res_multiplier), static_cast(scissor_y * res_multiplier) }, + { static_cast(scissor_w * res_multiplier), static_cast(scissor_h * res_multiplier) } }; break; case SCE_GXM_REGION_CLIP_INSIDE: @@ -153,7 +153,7 @@ void sync_point_line_width(VKContext &context, const bool is_front) { return; if (is_front && context.state.physical_device_features.wideLines) - context.render_cmd.setLineWidth(static_cast(context.record.line_width * context.state.res_multiplier)); + context.render_cmd.setLineWidth(context.record.line_width * context.state.res_multiplier); } void sync_viewport_flat(VKContext &context) { @@ -181,7 +181,7 @@ void sync_viewport_real(VKContext &context, const float xOffset, const float yOf const float x = xOffset - std::abs(xScale); const float y = yOffset - yScale; - const int res_multiplier = context.state.res_multiplier; + const float res_multiplier = context.state.res_multiplier; // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkViewport.html // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vertexpostproc-viewport