rsx/overlays: Introduce 'native' HUD UI and implement some common dialogs (#4011)

This commit is contained in:
kd-11 2018-01-17 19:14:00 +03:00 committed by GitHub
parent 34c49c77b2
commit 71f69d1d48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 3682 additions and 213 deletions

BIN
bin/Icons/ui/L1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
bin/Icons/ui/L2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
bin/Icons/ui/R1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
bin/Icons/ui/R2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
bin/Icons/ui/circle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
bin/Icons/ui/cross.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
bin/Icons/ui/dpad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
bin/Icons/ui/dpad_down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
bin/Icons/ui/dpad_left.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
bin/Icons/ui/dpad_right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
bin/Icons/ui/dpad_up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
bin/Icons/ui/fade_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
bin/Icons/ui/left_stick.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
bin/Icons/ui/new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
bin/Icons/ui/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
bin/Icons/ui/select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
bin/Icons/ui/square.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
bin/Icons/ui/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
bin/Icons/ui/triangle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -2,6 +2,7 @@
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/RSX/GSRender.h"
#include "cellSysutil.h"
#include "cellMsgDialog.h"
@ -60,13 +61,6 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
default: return CELL_MSGDIALOG_ERROR_PARAM;
}
const auto dlg = fxm::import<MsgDialogBase>(Emu.GetCallbacks().get_msg_dialog);
if (!dlg)
{
return CELL_SYSUTIL_ERROR_BUSY;
}
if (_type.se_mute_on)
{
// TODO
@ -81,6 +75,33 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
cellSysutil.error(msgString.get_ptr());
}
if (auto rsxthr = fxm::get<GSRender>())
{
if (auto dlg = rsxthr->shell_open_message_dialog())
{
dlg->show(msgString.get_ptr(), _type, [callback, userData](s32 status)
{
if (callback)
{
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
callback(ppu, status, userData);
return CELL_OK;
});
}
});
return CELL_OK;
}
}
const auto dlg = fxm::import<MsgDialogBase>(Emu.GetCallbacks().get_msg_dialog);
if (!dlg)
{
return CELL_SYSUTIL_ERROR_BUSY;
}
dlg->type = _type;
dlg->on_close = [callback, userData, wptr = std::weak_ptr<MsgDialogBase>(dlg)](s32 status)
@ -213,6 +234,33 @@ s32 cellMsgDialogClose(f32 delay)
{
cellSysutil.warning("cellMsgDialogClose(delay=%f)", delay);
extern u64 get_system_time();
const u64 wait_until = get_system_time() + static_cast<s64>(std::max<float>(delay, 0.0f) * 1000);
if (auto rsxthr = fxm::get<GSRender>())
{
if (auto dlg = rsxthr->shell_get_current_dialog())
{
thread_ctrl::spawn("cellMsgDialogClose() Thread", [=]
{
while (get_system_time() < wait_until)
{
if (Emu.IsStopped())
return;
if (rsxthr->shell_get_current_dialog() != dlg)
return;
std::this_thread::sleep_for(1ms);
}
dlg->close();
});
return CELL_OK;
}
}
const auto dlg = fxm::get<MsgDialogBase>();
if (!dlg)
@ -220,10 +268,6 @@ s32 cellMsgDialogClose(f32 delay)
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
}
extern u64 get_system_time();
const u64 wait_until = get_system_time() + static_cast<s64>(std::max<float>(delay, 0.0f) * 1000);
thread_ctrl::spawn("cellMsgDialogClose() Thread", [=]()
{
while (dlg->state == MsgDialogState::Open && get_system_time() < wait_until)
@ -243,6 +287,14 @@ s32 cellMsgDialogAbort()
{
cellSysutil.warning("cellMsgDialogAbort()");
if (auto rsxthr = fxm::get<GSRender>())
{
if (rsxthr->shell_close_dialog())
{
return CELL_OK;
}
}
const auto dlg = fxm::get<MsgDialogBase>();
if (!dlg)
@ -263,6 +315,15 @@ s32 cellMsgDialogProgressBarSetMsg(u32 progressBarIndex, vm::cptr<char> msgStrin
{
cellSysutil.warning("cellMsgDialogProgressBarSetMsg(progressBarIndex=%d, msgString=%s)", progressBarIndex, msgString);
if (auto rsxthr = fxm::get<GSRender>())
{
if (auto dlg2 = rsxthr->shell_get_current_dialog())
{
if (auto casted = dynamic_cast<rsx::overlays::message_dialog*>(dlg2))
return casted->progress_bar_set_message(progressBarIndex, msgString.get_ptr());
}
}
const auto dlg = fxm::get<MsgDialogBase>();
if (!dlg)
@ -287,6 +348,15 @@ s32 cellMsgDialogProgressBarReset(u32 progressBarIndex)
{
cellSysutil.warning("cellMsgDialogProgressBarReset(progressBarIndex=%d)", progressBarIndex);
if (auto rsxthr = fxm::get<GSRender>())
{
if (auto dlg2 = rsxthr->shell_get_current_dialog())
{
if (auto casted = dynamic_cast<rsx::overlays::message_dialog*>(dlg2))
return casted->progress_bar_reset(progressBarIndex);
}
}
const auto dlg = fxm::get<MsgDialogBase>();
if (!dlg)
@ -311,6 +381,15 @@ s32 cellMsgDialogProgressBarInc(u32 progressBarIndex, u32 delta)
{
cellSysutil.warning("cellMsgDialogProgressBarInc(progressBarIndex=%d, delta=%d)", progressBarIndex, delta);
if (auto rsxthr = fxm::get<GSRender>())
{
if (auto dlg2 = rsxthr->shell_get_current_dialog())
{
if (auto casted = dynamic_cast<rsx::overlays::message_dialog*>(dlg2))
return casted->progress_bar_increment(progressBarIndex, (f32)delta);
}
}
const auto dlg = fxm::get<MsgDialogBase>();
if (!dlg)

View file

@ -355,18 +355,7 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
while (funcList)
{
// Display Save Data List asynchronously in the GUI thread.
atomic_t<bool> dlg_result(false);
Emu.CallAfter([&]()
{
selected = Emu.GetCallbacks().get_save_dialog()->ShowSaveDataList(save_entries, focused, operation, listSet);
dlg_result = true;
});
while (!dlg_result)
{
thread_ctrl::wait_for(1000);
}
selected = Emu.GetCallbacks().get_save_dialog()->ShowSaveDataList(save_entries, focused, operation, listSet);
// UI returns -1 for new save games
if (selected == -1)

View file

@ -534,9 +534,8 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
sceNpTrophy.error("Failed to get info for trophy dialog. Error code %x", ret);
*details = SceNpTrophyDetails();
}
Emu.CallAfter([det = *details, trophyIconData]() {
Emu.GetCallbacks().get_trophy_notification_dialog()->ShowTrophyNotification(det, trophyIconData);
});
Emu.GetCallbacks().get_trophy_notification_dialog()->ShowTrophyNotification(*details, trophyIconData);
}
return CELL_OK;

View file

@ -301,7 +301,7 @@ void D3D12GSRender::on_exit()
return GSRender::on_exit();
}
void D3D12GSRender::do_local_task()
void D3D12GSRender::do_local_task(bool)
{
//TODO
m_frame->clear_wm_events();

View file

@ -173,7 +173,7 @@ private:
protected:
virtual void on_init_thread() override;
virtual void on_exit() override;
virtual void do_local_task() override;
virtual void do_local_task(bool idle) override;
virtual bool do_method(u32 cmd, u32 arg) override;
virtual void end() override;
virtual void flip(int buffer) override;

View file

@ -32,6 +32,7 @@ GLGSRender::GLGSRender() : GSRender()
m_vertex_cache.reset(new gl::weak_vertex_cache());
supports_multidraw = !g_cfg.video.strict_rendering_mode;
supports_native_ui = (bool)g_cfg.misc.use_native_interface;
}
extern CellGcmContextData current_context;
@ -614,9 +615,6 @@ void GLGSRender::on_init_thread()
{
GSRender::on_init_thread();
m_frame->disable_wm_event_queue();
m_frame->hide();
gl::init();
//Enable adaptive vsync if vsync is requested
@ -756,16 +754,71 @@ void GLGSRender::on_init_thread()
glEnable(GL_CLIP_DISTANCE0 + 5);
m_depth_converter.create();
m_ui_renderer.create();
m_gl_texture_cache.initialize();
m_thread_id = std::this_thread::get_id();
m_shaders_cache->load();
if (!supports_native_ui)
{
m_frame->disable_wm_event_queue();
m_frame->hide();
m_frame->enable_wm_event_queue();
m_frame->show();
m_shaders_cache->load(nullptr);
m_frame->enable_wm_event_queue();
m_frame->show();
}
else
{
struct native_helper : gl::shader_cache::progress_dialog_helper
{
rsx::thread *owner = nullptr;
rsx::overlays::message_dialog *dlg = nullptr;
native_helper(GLGSRender *ptr) :
owner(ptr) {}
void create() override
{
MsgDialogType type = {};
type.disable_cancel = true;
type.progress_bar_count = 1;
dlg = owner->shell_open_message_dialog();
dlg->show("Loading precompiled shaders from disk...", type, [](s32 status)
{
if (status != CELL_OK)
Emu.Stop();
});
}
void update_msg(u32 processed, u32 entry_count) override
{
dlg->progress_bar_set_message(0, fmt::format("Loading pipeline object %u of %u", processed, entry_count));
owner->flip(0);
}
void inc_value(u32 value) override
{
dlg->progress_bar_increment(0, (f32)value);
owner->flip(0);
}
void close() override
{
dlg->return_code = CELL_OK;
dlg->close();
}
}
helper(this);
m_frame->enable_wm_event_queue();
m_shaders_cache->load(&helper);
}
}
void GLGSRender::on_exit()
{
m_prog_buffer.clear();
@ -826,6 +879,7 @@ void GLGSRender::on_exit()
m_text_printer.close();
m_gl_texture_cache.destroy();
m_depth_converter.destroy();
m_ui_renderer.destroy();
for (u32 i = 0; i < occlusion_query_count; ++i)
{
@ -963,8 +1017,22 @@ void GLGSRender::load_program(u32 vertex_base, u32 vertex_count)
m_program->use();
if (m_prog_buffer.check_cache_missed())
{
m_shaders_cache->store(pipeline_properties, vertex_program, fragment_program);
//Notify the user with HUD notification
if (!m_custom_ui)
{
//Create notification but do not draw it at this time. No need to spam flip requests
m_custom_ui = std::make_unique<rsx::overlays::shader_compile_notification>();
}
else if (auto casted = dynamic_cast<rsx::overlays::shader_compile_notification*>(m_custom_ui.get()))
{
//Probe the notification
casted->touch();
}
}
u8 *buf;
u32 vertex_state_offset;
u32 vertex_constants_offset;
@ -1244,6 +1312,13 @@ void GLGSRender::flip(int buffer)
gl::screen.clear(gl::buffers::color);
m_flip_fbo.blit(gl::screen, screen_area, areai(aspect_ratio).flipped_vertical(), gl::buffers::color, gl::filter::linear);
if (m_custom_ui)
{
gl::screen.bind();
glViewport(0, 0, m_frame->client_width(), m_frame->client_height());
m_ui_renderer.run(m_frame->client_width(), m_frame->client_height(), 0, *m_custom_ui.get());
}
if (g_cfg.video.overlay)
{
gl::screen.bind();
@ -1334,7 +1409,7 @@ void GLGSRender::on_notify_memory_unmapped(u32 address_base, u32 size)
}
}
void GLGSRender::do_local_task()
void GLGSRender::do_local_task(bool idle)
{
m_frame->clear_wm_events();
@ -1354,6 +1429,15 @@ void GLGSRender::do_local_task()
lock.unlock();
q.cv.notify_one();
}
if (m_custom_ui)
{
if (!in_begin_end && idle && native_ui_flip_request.load())
{
native_ui_flip_request.store(false);
flip((s32)current_display_buffer);
}
}
}
work_item& GLGSRender::post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data)
@ -1415,4 +1499,9 @@ void GLGSRender::get_occlusion_query_result(rsx::occlusion_query_info* query)
glGetQueryObjectiv((GLuint)query->driver_handle, GL_QUERY_RESULT, &result);
query->result += result;
}
void GLGSRender::shell_do_cleanup()
{
m_ui_renderer.remove_temp_resources();
}

View file

@ -294,6 +294,7 @@ private:
gl::text_writer m_text_printer;
gl::depth_convert_pass m_depth_converter;
gl::ui_overlay_renderer m_ui_renderer;
std::mutex queue_guard;
std::list<work_item> work_queue;
@ -361,12 +362,14 @@ protected:
void flip(int buffer) override;
u64 timestamp() const override;
void do_local_task() override;
void do_local_task(bool idle) override;
bool on_access_violation(u32 address, bool is_writing) override;
void on_notify_memory_unmapped(u32 address_base, u32 size) override;
void notify_tile_unbound(u32 tile) override;
virtual std::array<std::vector<gsl::byte>, 4> copy_render_targets_to_memory() override;
virtual std::array<std::vector<gsl::byte>, 2> copy_depth_stencil_buffer_to_memory() override;
std::array<std::vector<gsl::byte>, 4> copy_render_targets_to_memory() override;
std::array<std::vector<gsl::byte>, 2> copy_depth_stencil_buffer_to_memory() override;
void shell_do_cleanup() override;
};

View file

@ -557,4 +557,9 @@ namespace gl
fmt::throw_exception("unknown primitive type" HERE);
}
}
attrib_t vao::operator[](u32 index) const noexcept
{
return attrib_t(index);
}
}

View file

@ -58,6 +58,8 @@ namespace gl
void enable_debugging();
capabilities& get_driver_caps();
bool is_primitive_native(rsx::primitive_type in);
GLenum draw_mode(rsx::primitive_type in);
class exception : public std::exception
{
@ -489,6 +491,7 @@ namespace gl
};
class vao;
class attrib_t;
class buffer_pointer
{
@ -1167,6 +1170,43 @@ namespace gl
{
return{ (vao*)this };
}
attrib_t operator [] (u32 index) const noexcept;
};
class attrib_t
{
GLint m_location;
public:
attrib_t(GLint location)
: m_location(location)
{
}
GLint location() const
{
return m_location;
}
void operator = (float rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs); }
void operator = (double rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs); }
void operator = (const color1f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs.r); }
void operator = (const color1d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs.r); }
void operator = (const color2f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2f(location(), rhs.r, rhs.g); }
void operator = (const color2d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2d(location(), rhs.r, rhs.g); }
void operator = (const color3f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3f(location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color3d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3d(location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color4f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4f(location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (const color4d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4d(location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (buffer_pointer& pointer) const
{
pointer.get_vao().enable_for_attribute(m_location);
glVertexAttribPointer(location(), pointer.size(), (GLenum)pointer.get_type(), pointer.normalize(),
pointer.stride(), (const void*)(size_t)pointer.offset());
}
};
class texture_view;
@ -1221,8 +1261,7 @@ namespace gl
enum class format
{
red = GL_RED,
r = GL_R,
r = GL_RED,
rg = GL_RG,
rgb = GL_RGB,
rgba = GL_RGBA,
@ -1629,9 +1668,9 @@ namespace gl
copy_from(nullptr, format, type, pixel_settings);
}
void copy_from(void* dst, texture::format format, texture::type type)
void copy_from(void* src, texture::format format, texture::type type)
{
copy_from(dst, format, type, pixel_unpack_settings());
copy_from(src, format, type, pixel_unpack_settings());
}
void copy_from(const buffer& buf, texture::format format, texture::type type)
@ -1872,9 +1911,6 @@ namespace gl
settings& border_color(color4f value);
};
GLenum draw_mode(rsx::primitive_type in);
bool is_primitive_native(rsx::primitive_type in);
enum class indices_type
{
ubyte = GL_UNSIGNED_BYTE,
@ -2270,43 +2306,8 @@ namespace gl
void operator = (const color3f& rhs) const { glProgramUniform3f(m_program.id(), location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color4i& rhs) const { glProgramUniform4i(m_program.id(), location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (const color4f& rhs) const { glProgramUniform4f(m_program.id(), location(), rhs.r, rhs.g, rhs.b, rhs.a); }
};
class attrib_t
{
GLuint m_program;
GLint m_location;
public:
attrib_t(GLuint program, GLint location)
: m_program(program)
, m_location(location)
{
}
GLint location() const
{
return m_location;
}
void operator = (float rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs); }
void operator = (double rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs); }
void operator = (const color1f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs.r); }
void operator = (const color1d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs.r); }
void operator = (const color2f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2f(location(), rhs.r, rhs.g); }
void operator = (const color2d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2d(location(), rhs.r, rhs.g); }
void operator = (const color3f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3f(location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color3d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3d(location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color4f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4f(location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (const color4d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4d(location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator =(buffer_pointer& pointer) const
{
pointer.get_vao().enable_for_attribute(location());
glVertexAttribPointer(location(), pointer.size(), (GLenum)pointer.get_type(), pointer.normalize(),
pointer.stride(), (const void*)(size_t)pointer.offset());
}
void operator = (const areaf& rhs) const { glProgramUniform4f(m_program.id(), location(), rhs.x1, rhs.y1, rhs.x2, rhs.y2); }
void operator = (const areai& rhs) const { glProgramUniform4i(m_program.id(), location(), rhs.x1, rhs.y1, rhs.x2, rhs.y2); }
};
class uniforms_t
@ -2471,12 +2472,12 @@ namespace gl
attrib_t operator[](GLint location)
{
return{ m_program.id(), location };
return{ location };
}
attrib_t operator[](const std::string &name)
{
return{ m_program.id(), location(name) };
return{ location(name) };
}
void swap(attribs_t& attribs)

View file

@ -2,6 +2,9 @@
#include "stdafx.h"
#include "GLHelpers.h"
#include "../overlays.h"
extern u64 get_system_time();
namespace gl
{
@ -16,8 +19,14 @@ namespace gl
gl::fbo fbo;
gl::vao m_vao;
gl::buffer m_vertex_data_buffer;
bool compiled = false;
u32 num_drawable_elements = 4;
GLenum primitives = GL_TRIANGLE_STRIP;
void create()
{
if (!compiled)
@ -37,6 +46,20 @@ namespace gl
fbo.create();
m_vertex_data_buffer.create();
int old_vao;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
m_vao.create();
m_vao.bind();
m_vao.array_buffer = m_vertex_data_buffer;
auto ptr = buffer_pointer(&m_vao);
m_vao[0] = ptr;
glBindVertexArray(old_vao);
compiled = true;
}
}
@ -50,6 +73,8 @@ namespace gl
fs.remove();
fbo.remove();
m_vao.remove();
m_vertex_data_buffer.remove();
compiled = false;
}
@ -61,12 +86,24 @@ namespace gl
virtual void bind_resources() {}
virtual void cleanup_resources() {}
virtual void emit_geometry()
virtual void upload_vertex_data(f32* data, u32 elements_count)
{
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
elements_count <<= 2;
m_vertex_data_buffer.data(elements_count, data);
}
virtual void run(u16 w, u16 h, GLuint target_texture, bool depth_target)
virtual void emit_geometry()
{
int old_vao;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
m_vao.bind();
glDrawArrays(primitives, 0, num_drawable_elements);
glBindVertexArray(old_vao);
}
virtual void run(u16 w, u16 h, GLuint target_texture, bool depth_target, bool use_blending = false)
{
if (!compiled)
{
@ -81,22 +118,32 @@ namespace gl
GLboolean color_writes[4];
GLboolean depth_write;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo.id());
GLint blend_src_rgb;
GLint blend_src_a;
GLint blend_dst_rgb;
GLint blend_dst_a;
GLint blend_eq_a;
GLint blend_eq_rgb;
if (depth_target)
if (target_texture)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, target_texture, 0);
glDrawBuffer(GL_NONE);
}
else
{
GLenum buffer = GL_COLOR_ATTACHMENT0;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0);
glDrawBuffers(1, &buffer);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo.id());
if (depth_target)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, target_texture, 0);
glDrawBuffer(GL_NONE);
}
else
{
GLenum buffer = GL_COLOR_ATTACHMENT0;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0);
glDrawBuffers(1, &buffer);
}
}
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
if (!target_texture || glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
{
// Push rasterizer state
glGetIntegerv(GL_VIEWPORT, viewport);
@ -111,6 +158,16 @@ namespace gl
GLboolean blend_enabled = glIsEnabled(GL_BLEND);
GLboolean stencil_test_enabled = glIsEnabled(GL_STENCIL_TEST);
if (use_blending)
{
glGetIntegerv(GL_BLEND_SRC_RGB, &blend_src_rgb);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &blend_src_a);
glGetIntegerv(GL_BLEND_DST_RGB, &blend_dst_rgb);
glGetIntegerv(GL_BLEND_DST_ALPHA, &blend_dst_a);
glGetIntegerv(GL_BLEND_EQUATION_RGB, &blend_eq_rgb);
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blend_eq_a);
}
// Set initial state
glViewport(0, 0, w, h);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@ -122,9 +179,21 @@ namespace gl
if (scissor_enabled) glDisable(GL_SCISSOR_TEST);
if (cull_face_enabled) glDisable(GL_CULL_FACE);
if (blend_enabled) glDisable(GL_BLEND);
if (stencil_test_enabled) glDisable(GL_STENCIL_TEST);
if (use_blending)
{
if (!blend_enabled)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
}
else if (blend_enabled)
{
glDisable(GL_BLEND);
}
// Render
program_handle.use();
on_load();
@ -132,12 +201,16 @@ namespace gl
emit_geometry();
// Clean up
if (depth_target)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
else
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
if (target_texture)
{
if (depth_target)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
else
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glBindFramebuffer(GL_FRAMEBUFFER, old_fbo);
}
glBindFramebuffer(GL_FRAMEBUFFER, old_fbo);
glUseProgram((GLuint)program);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
@ -148,8 +221,20 @@ namespace gl
if (!depth_test_enabled) glDisable(GL_DEPTH_TEST);
if (scissor_enabled) glEnable(GL_SCISSOR_TEST);
if (cull_face_enabled) glEnable(GL_CULL_FACE);
if (blend_enabled) glEnable(GL_BLEND);
if (stencil_test_enabled) glEnable(GL_STENCIL_TEST);
if (use_blending)
{
if (!blend_enabled)
glDisable(GL_BLEND);
glBlendFuncSeparate(blend_src_rgb, blend_dst_rgb, blend_src_a, blend_dst_a);
glBlendEquationSeparate(blend_eq_rgb, blend_eq_a);
}
else if (blend_enabled)
{
glEnable(GL_BLEND);
}
}
else
{
@ -237,4 +322,251 @@ namespace gl
overlay_pass::run(w, h, target, false);
}
};
}
struct ui_overlay_renderer : public overlay_pass
{
u32 num_elements = 0;
std::vector<std::unique_ptr<gl::texture>> resources;
std::unordered_map<u64, std::unique_ptr<gl::texture>> temp_image_cache;
std::unordered_map<u64, std::unique_ptr<gl::texture>> font_cache;
bool is_font_draw = false;
ui_overlay_renderer()
{
vs_src =
{
"#version 420\n\n"
"layout(location=0) in vec4 in_pos;\n"
"layout(location=0) out vec2 tc0;\n"
"layout(location=1) out vec4 clip_rect;\n"
"uniform vec4 ui_scale;\n"
"uniform vec4 clip_bounds;\n"
"\n"
"void main()\n"
"{\n"
" tc0.xy = in_pos.zw;\n"
" clip_rect = (clip_bounds * ui_scale.zwzw);\n"
" clip_rect.yw = ui_scale.yy - clip_rect.wy; //invert y axis\n"
" vec4 pos = vec4((in_pos.xy * ui_scale.zw) / ui_scale.xy, 0., 1.);\n"
" pos.y = (1. - pos.y); //invert y axis\n"
" gl_Position = (pos + pos) - 1.;\n"
"}\n"
};
fs_src =
{
"#version 420\n\n"
"layout(binding=31) uniform sampler2D fs0;\n"
"layout(location=0) in vec2 tc0;\n"
"layout(location=1) in vec4 clip_rect;\n"
"layout(location=0) out vec4 ocol;\n"
"uniform vec4 color;\n"
"uniform float time;\n"
"uniform int read_texture;\n"
"uniform int pulse_glow;\n"
"uniform int clip_region;\n"
"\n"
"void main()\n"
"{\n"
" if (clip_region != 0)\n"
" {"
" if (gl_FragCoord.x < clip_rect.x || gl_FragCoord.x > clip_rect.z ||\n"
" gl_FragCoord.y < clip_rect.y || gl_FragCoord.y > clip_rect.w)\n"
" {\n"
" discard;\n"
" return;\n"
" }\n"
" }\n"
"\n"
" vec4 diff_color = color;\n"
" if (pulse_glow != 0)\n"
" diff_color.a *= (sin(time) + 1.f) * 0.5f;\n"
"\n"
" if (read_texture != 0)\n"
" ocol = texture(fs0, tc0) * diff_color;\n"
" else\n"
" ocol = diff_color;\n"
"}\n"
};
}
gl::texture* load_simple_image(rsx::overlays::image_info* desc, bool temp_resource)
{
auto tex = std::make_unique<gl::texture>(gl::texture::target::texture2D);
tex->create();
tex->config()
.size({ desc->w, desc->h })
.format(gl::texture::format::rgba)
.type(gl::texture::type::uint_8_8_8_8)
.wrap(gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border)
.swizzle(gl::texture::channel::a, gl::texture::channel::b, gl::texture::channel::g, gl::texture::channel::r)
.filter(gl::min_filter::linear, gl::filter::linear)
.apply();
tex->copy_from(desc->data, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8);
if (!temp_resource)
{
resources.push_back(std::move(tex));
}
else
{
u64 key = (u64)desc;
temp_image_cache[key] = std::move(tex);
}
return resources.back().get();
}
void create()
{
overlay_pass::create();
rsx::overlays::resource_config configuration;
configuration.load_files();
for (const auto &res : configuration.texture_raw_data)
{
load_simple_image(res.get(), false);
}
configuration.free_resources();
}
void destroy()
{
temp_image_cache.clear();
resources.clear();
font_cache.clear();
overlay_pass::destroy();
}
void remove_temp_resources()
{
temp_image_cache.clear();
}
gl::texture* find_font(rsx::overlays::font *font)
{
u64 key = (u64)font;
auto found = font_cache.find(key);
if (found != font_cache.end())
return found->second.get();
//Create font file
auto tex = std::make_unique<gl::texture>(gl::texture::target::texture2D);
tex->create();
tex->config()
.size({ (int)font->width, (int)font->height })
.format(gl::texture::format::r)
.type(gl::texture::type::ubyte)
.internal_format(gl::texture::internal_format::r8)
.wrap(gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border)
.swizzle(gl::texture::channel::r, gl::texture::channel::r, gl::texture::channel::r, gl::texture::channel::r)
.filter(gl::min_filter::linear, gl::filter::linear)
.apply();
tex->copy_from(font->glyph_data.data(), gl::texture::format::r, gl::texture::type::ubyte);
auto result = tex.get();
font_cache[key] = std::move(tex);
return result;
}
gl::texture* find_temp_image(rsx::overlays::image_info *desc)
{
auto key = (u64)desc;
auto cached = temp_image_cache.find(key);
if (cached != temp_image_cache.end())
{
return cached->second.get();
}
else
{
return load_simple_image(desc, true);
}
}
void emit_geometry() override
{
if (!is_font_draw)
{
overlay_pass::emit_geometry();
}
else
{
int num_quads = num_drawable_elements / 4;
std::vector<GLint> firsts;
std::vector<GLsizei> counts;
firsts.resize(num_quads);
counts.resize(num_quads);
for (int n = 0; n < num_quads; ++n)
{
firsts[n] = (n * 4);
counts[n] = 4;
}
int old_vao;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
m_vao.bind();
glMultiDrawArrays(GL_TRIANGLE_STRIP, firsts.data(), counts.data(), num_quads);
glBindVertexArray(old_vao);
}
}
void run(u16 w, u16 h, GLuint target, rsx::overlays::user_interface& ui)
{
program_handle.uniforms["ui_scale"] = color4f((f32)ui.virtual_width, (f32)ui.virtual_height, 1.f, 1.f);
program_handle.uniforms["time"] = (f32)(get_system_time() / 1000) * 0.005f;
for (auto &cmd : ui.get_compiled().draw_commands)
{
upload_vertex_data((f32*)cmd.second.data(), (u32)cmd.second.size() * 4u);
num_drawable_elements = (u32)cmd.second.size();
is_font_draw = false;
GLint texture_exists = GL_TRUE;
glActiveTexture(GL_TEXTURE31);
switch (cmd.first.texture_ref)
{
case rsx::overlays::image_resource_id::game_icon:
case rsx::overlays::image_resource_id::backbuffer:
//TODO
case rsx::overlays::image_resource_id::none:
{
texture_exists = GL_FALSE;
glBindTexture(GL_TEXTURE_2D, GL_NONE);
break;
}
case rsx::overlays::image_resource_id::raw_image:
{
glBindTexture(GL_TEXTURE_2D, find_temp_image((rsx::overlays::image_info*)cmd.first.external_data_ref)->id());
break;
}
case rsx::overlays::image_resource_id::font_file:
{
is_font_draw = true;
glBindTexture(GL_TEXTURE_2D, find_font(cmd.first.font_ref)->id());
break;
}
default:
{
glBindTexture(GL_TEXTURE_2D, resources[cmd.first.texture_ref - 1]->id());
break;
}
}
program_handle.uniforms["color"] = cmd.first.color;
program_handle.uniforms["read_texture"] = texture_exists;
program_handle.uniforms["pulse_glow"] = (s32)cmd.first.pulse_glow;
program_handle.uniforms["clip_region"] = (s32)cmd.first.clip_region;
program_handle.uniforms["clip_bounds"] = cmd.first.clip_rect;
overlay_pass::run(w, h, target, false, true);
}
ui.update();
}
};
}

View file

@ -31,13 +31,13 @@ color_format rsx::internals::surface_color_format_to_gl(rsx::surface_color_forma
return{ ::gl::texture::type::f32, ::gl::texture::format::rgba, true, 4, 4 };
case rsx::surface_color_format::b8:
return{ ::gl::texture::type::ubyte, ::gl::texture::format::red, false, 1, 1 };
return{ ::gl::texture::type::ubyte, ::gl::texture::format::r, false, 1, 1 };
case rsx::surface_color_format::g8b8:
return{ ::gl::texture::type::ubyte, ::gl::texture::format::rg, false, 2, 1 };
case rsx::surface_color_format::x32:
return{ ::gl::texture::type::f32, ::gl::texture::format::red, true, 1, 4 };
return{ ::gl::texture::type::f32, ::gl::texture::format::r, true, 1, 4 };
default:
LOG_ERROR(RSX, "Surface color buffer: Unsupported surface color format (0x%x)", (u32)color_format);

View file

@ -142,7 +142,6 @@ namespace gl
switch (fmt_)
{
case texture::format::red:
case texture::format::r:
break;
case texture::format::rg:

View file

@ -481,9 +481,6 @@ namespace rsx
// TODO: exit condition
while (!Emu.IsStopped())
{
//Execute backend-local tasks first
do_local_task();
//Wait for external pause events
if (external_interrupt_lock.load())
{
@ -491,6 +488,9 @@ namespace rsx
while (external_interrupt_lock.load()) _mm_pause();
}
//Execute backend-local tasks first
do_local_task(ctrl->put.load() == internal_get.load());
//Set up restore state if needed
if (sync_point_request)
{
@ -2179,7 +2179,13 @@ namespace rsx
void thread::pause()
{
external_interrupt_lock.store(true);
while (!external_interrupt_ack.load()) _mm_pause();
while (!external_interrupt_ack.load())
{
if (Emu.IsStopped())
break;
_mm_pause();
}
external_interrupt_ack.store(false);
}
@ -2187,4 +2193,67 @@ namespace rsx
{
external_interrupt_lock.store(false);
}
//TODO: Move these helpers into a better class dedicated to shell interface handling (use idm?)
//They are not dependent on rsx at all
rsx::overlays::save_dialog* thread::shell_open_save_dialog()
{
if (supports_native_ui)
{
auto ptr = new rsx::overlays::save_dialog();
m_custom_ui.reset(ptr);
return ptr;
}
else
{
return nullptr;
}
}
rsx::overlays::message_dialog* thread::shell_open_message_dialog()
{
if (supports_native_ui)
{
auto ptr = new rsx::overlays::message_dialog();
m_custom_ui.reset(ptr);
return ptr;
}
else
{
return nullptr;
}
}
rsx::overlays::trophy_notification* thread::shell_open_trophy_notification()
{
if (supports_native_ui)
{
auto ptr = new rsx::overlays::trophy_notification();
m_custom_ui.reset(ptr);
return ptr;
}
else
{
return nullptr;
}
}
rsx::overlays::user_interface* thread::shell_get_current_dialog()
{
//TODO: Only get dialog type interfaces
return m_custom_ui.get();
}
bool thread::shell_close_dialog()
{
//TODO: Only get dialog type interfaces
if (m_custom_ui)
{
m_invalidated_ui = std::move(m_custom_ui);
shell_do_cleanup();
return true;
}
return false;
}
}

View file

@ -12,6 +12,7 @@
#include "RSXFragmentProgram.h"
#include "rsx_methods.h"
#include "rsx_utils.h"
#include "overlays.h"
#include <Utilities/GSL.h>
#include "Utilities/Thread.h"
@ -216,6 +217,7 @@ namespace rsx
bool skip_frame = false;
bool supports_multidraw = false;
bool supports_native_ui = false;
//occlusion query
bool zcull_surface_active = false;
@ -230,6 +232,9 @@ namespace rsx
rsx::gcm_framebuffer_info m_depth_surface_info;
bool framebuffer_status_valid = false;
std::unique_ptr<rsx::overlays::user_interface> m_custom_ui;
std::unique_ptr<rsx::overlays::user_interface> m_invalidated_ui;
public:
RsxDmaControl* ctrl = nullptr;
atomic_t<u32> internal_get{ 0 };
@ -237,6 +242,9 @@ namespace rsx
atomic_t<bool> external_interrupt_lock{ false };
atomic_t<bool> external_interrupt_ack{ false };
//native UI interrupts
atomic_t<bool> native_ui_flip_request{ false };
GcmTileInfo tiles[limits::tiles_count];
GcmZcullInfo zculls[limits::zculls_count];
@ -328,8 +336,9 @@ namespace rsx
/**
* Execute a backend local task queue
* Idle argument checks that the FIFO queue is in an idle state
*/
virtual void do_local_task() {}
virtual void do_local_task(bool idle) {}
public:
virtual std::string get_name() const override;
@ -419,6 +428,8 @@ namespace rsx
public:
//std::future<void> add_internal_task(std::function<bool()> callback);
//void invoke(std::function<bool()> callback);
void add_user_interface(std::shared_ptr<rsx::overlays::user_interface> iface);
void remove_user_interface();
/**
* Fill buffer with 4x4 scale offset matrix.
@ -484,5 +495,14 @@ namespace rsx
void pause();
void unpause();
//HLE vsh stuff
//TODO: Move into a separate helper
virtual rsx::overlays::save_dialog* shell_open_save_dialog();
virtual rsx::overlays::message_dialog* shell_open_message_dialog();
virtual rsx::overlays::trophy_notification* shell_open_trophy_notification();
virtual rsx::overlays::user_interface* shell_get_current_dialog();
virtual bool shell_close_dialog();
virtual void shell_do_cleanup(){}
};
}

View file

@ -646,25 +646,30 @@ VKGSRender::VKGSRender() : GSRender()
for (u32 i = 0; i < m_swap_chain->get_swap_image_count(); ++i)
{
VkClearColorValue clear_color{};
VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i),
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT));
range);
VkClearColorValue clear_color{};
auto range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range);
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i),
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT));
range);
}
m_current_frame = &frame_context_storage[0];
m_texture_cache.initialize((*m_device), m_memory_type_mapping, m_optimal_tiling_supported_formats, m_swap_chain->get_present_queue(),
m_texture_upload_buffer_ring_info, m_texture_upload_buffer_ring_info.heap.get());
m_texture_upload_buffer_ring_info);
m_ui_renderer.reset(new vk::ui_overlay_renderer());
m_ui_renderer->create(*m_current_command_buffer, m_memory_type_mapping, m_texture_upload_buffer_ring_info);
supports_multidraw = !g_cfg.video.strict_rendering_mode;
supports_native_ui = (bool)g_cfg.misc.use_native_interface;
}
VKGSRender::~VKGSRender()
@ -743,6 +748,10 @@ VKGSRender::~VKGSRender()
//Overlay text handler
m_text_writer.reset();
//Overlay UI renderer
m_ui_renderer->destroy();
m_ui_renderer.reset();
//RGBA->depth cast helper
m_depth_converter->destroy();
m_depth_converter.reset();
@ -1597,13 +1606,63 @@ void VKGSRender::on_init_thread()
GSRender::on_init_thread();
rsx_thread = std::this_thread::get_id();
m_frame->disable_wm_event_queue();
m_frame->hide();
if (!supports_native_ui)
{
m_frame->disable_wm_event_queue();
m_frame->hide();
m_shaders_cache->load(nullptr, *m_device, pipeline_layout);
m_frame->enable_wm_event_queue();
m_frame->show();
}
else
{
struct native_helper : vk::shader_cache::progress_dialog_helper
{
rsx::thread *owner = nullptr;
rsx::overlays::message_dialog *dlg = nullptr;
m_shaders_cache->load(*m_device, pipeline_layout);
native_helper(VKGSRender *ptr) :
owner(ptr) {}
m_frame->enable_wm_event_queue();
m_frame->show();
void create() override
{
MsgDialogType type = {};
type.disable_cancel = true;
type.progress_bar_count = 1;
dlg = owner->shell_open_message_dialog();
dlg->show("Loading precompiled shaders from disk...", type, [](s32 status)
{
if (status != CELL_OK)
Emu.Stop();
});
}
void update_msg(u32 processed, u32 entry_count) override
{
dlg->progress_bar_set_message(0, fmt::format("Loading pipeline object %u of %u", processed, entry_count));
owner->flip(0);
}
void inc_value(u32 value) override
{
dlg->progress_bar_increment(0, (f32)value);
owner->flip(0);
}
void close() override
{
dlg->return_code = CELL_OK;
dlg->close();
}
}
helper(this);
//TODO: Handle window resize messages during loading on GPUs without OUT_OF_DATE_KHR support
m_frame->disable_wm_event_queue();
m_shaders_cache->load(&helper, *m_device, pipeline_layout);
m_frame->enable_wm_event_queue();
}
}
void VKGSRender::on_exit()
@ -1951,6 +2010,7 @@ void VKGSRender::process_swap_request(frame_context_t *ctx, bool free_resources)
}
m_depth_converter->free_resources();
m_ui_renderer->free_resources();
ctx->buffer_views_to_clean.clear();
ctx->samplers_to_clean.clear();
@ -1975,8 +2035,16 @@ void VKGSRender::process_swap_request(frame_context_t *ctx, bool free_resources)
ctx->swap_command_buffer = nullptr;
}
void VKGSRender::do_local_task()
void VKGSRender::do_local_task(bool idle)
{
//TODO: Guard this
if (m_overlay_cleanup_requests.size())
{
flush_command_queue(true);
m_ui_renderer->remove_temp_resources();
m_overlay_cleanup_requests.clear();
}
if (m_flush_commands)
{
std::lock_guard<std::mutex> lock(m_flush_queue_mutex);
@ -2082,6 +2150,16 @@ void VKGSRender::do_local_task()
}
#endif
if (m_custom_ui)
{
if (!in_begin_end && native_ui_flip_request.load())
{
native_ui_flip_request.store(false);
flush_command_queue(true);
flip((s32)current_display_buffer);
}
}
}
bool VKGSRender::do_method(u32 cmd, u32 arg)
@ -2255,8 +2333,22 @@ void VKGSRender::load_program(u32 vertex_count, u32 vertex_base)
m_program = m_prog_buffer->getGraphicPipelineState(vertex_program, fragment_program, properties, *m_device, pipeline_layout).get();
if (m_prog_buffer->check_cache_missed())
{
m_shaders_cache->store(properties, vertex_program, fragment_program);
//Notify the user with HUD notification
if (!m_custom_ui)
{
//Create notification but do not draw it at this time. No need to spam flip requests
m_custom_ui = std::make_unique<rsx::overlays::shader_compile_notification>();
}
else if (auto casted = dynamic_cast<rsx::overlays::shader_compile_notification*>(m_custom_ui.get()))
{
//Probe the notification
casted->touch();
}
}
vk::leave_uninterruptible();
const size_t fragment_constants_sz = m_prog_buffer->get_fragment_constants_buffer_size(fragment_program);
@ -2740,14 +2832,14 @@ void VKGSRender::reinitialize_swapchain()
{
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i),
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT));
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
VkClearColorValue clear_color{};
auto range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range);
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i),
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT));
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
}
//Will have to block until rendering is completed
@ -2827,9 +2919,6 @@ void VKGSRender::flip(int buffer)
u32 buffer_width = display_buffers[buffer].width;
u32 buffer_height = display_buffers[buffer].height;
u32 buffer_pitch = display_buffers[buffer].pitch;
areai screen_area = coordi({}, { (int)buffer_width, (int)buffer_height });
coordi aspect_ratio;
@ -2905,22 +2994,49 @@ void VKGSRender::flip(int buffer)
//Blit contents to screen..
vk::image* image_to_flip = nullptr;
if (std::get<1>(m_rtts.m_bound_render_targets[0]) != nullptr)
image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[0]);
else if (std::get<1>(m_rtts.m_bound_render_targets[1]) != nullptr)
image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[1]);
rsx::tiled_region buffer_region = get_tiled_address(display_buffers[buffer].offset, CELL_GCM_LOCATION_LOCAL);
u32 absolute_address = buffer_region.address + buffer_region.base;
if (auto render_target_texture = m_rtts.get_texture_from_render_target_if_applicable(absolute_address))
{
image_to_flip = render_target_texture;
}
else if (auto surface = m_texture_cache.find_texture_from_dimensions(absolute_address))
{
//Hack - this should be the first location to check for output
//The render might have been done offscreen or in software and a blit used to display
image_to_flip = surface->get_raw_texture();
}
VkImage target_image = m_swap_chain->get_swap_chain_image(m_current_frame->present_image);
if (image_to_flip)
{
vk::copy_scaled_image(*m_current_command_buffer, image_to_flip->value, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VkImageLayout target_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
if (aspect_ratio.x)
{
VkClearColorValue clear_black {};
vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range);
vkCmdClearColorImage(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_black, 1, &range);
target_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
}
vk::copy_scaled_image(*m_current_command_buffer, image_to_flip->value, target_image, image_to_flip->current_layout, target_layout,
0, 0, image_to_flip->width(), image_to_flip->height(), aspect_ratio.x, aspect_ratio.y, aspect_ratio.width, aspect_ratio.height, 1, VK_IMAGE_ASPECT_COLOR_BIT, false);
if (target_layout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
{
vk::change_image_layout(*m_current_command_buffer, target_image, target_layout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range);
}
}
else
{
//No draw call was issued!
VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
VkClearColorValue clear_black = { 0 };
//TODO: Upload raw bytes from cpu for rendering
VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
VkClearColorValue clear_black {};
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_GENERAL, range);
vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, &clear_black, 1, &range);
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range);
@ -2928,10 +3044,10 @@ void VKGSRender::flip(int buffer)
std::unique_ptr<vk::framebuffer_holder> direct_fbo;
std::vector<std::unique_ptr<vk::image_view>> swap_image_view;
if (g_cfg.video.overlay)
if (g_cfg.video.overlay || m_custom_ui)
{
//Change the image layout whilst setting up a dependency on waiting for the blit op to finish before we start writing
auto subres = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
VkImageSubresourceRange subres = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
@ -2966,19 +3082,27 @@ void VKGSRender::flip(int buffer)
direct_fbo.reset(new vk::framebuffer_holder(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view)));
}
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 54, direct_fbo->width(), direct_fbo->height(), "texture upload time: " + std::to_string(m_textures_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
if (m_custom_ui)
{
m_ui_renderer->run(*m_current_command_buffer, direct_fbo->width(), direct_fbo->height(), direct_fbo.get(), single_target_pass, m_memory_type_mapping, m_texture_upload_buffer_ring_info, *m_custom_ui);
}
auto num_dirty_textures = m_texture_cache.get_unreleased_textures_count();
auto texture_memory_size = m_texture_cache.get_texture_memory_in_use() / (1024 * 1024);
auto tmp_texture_memory_size = m_texture_cache.get_temporary_memory_in_use() / (1024 * 1024);
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 126, direct_fbo->width(), direct_fbo->height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 144, direct_fbo->width(), direct_fbo->height(), "Texture cache memory: " + std::to_string(texture_memory_size) + "M");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 162, direct_fbo->width(), direct_fbo->height(), "Temporary texture memory: " + std::to_string(tmp_texture_memory_size) + "M");
if (g_cfg.video.overlay)
{
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 54, direct_fbo->width(), direct_fbo->height(), "texture upload time: " + std::to_string(m_textures_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
auto num_dirty_textures = m_texture_cache.get_unreleased_textures_count();
auto texture_memory_size = m_texture_cache.get_texture_memory_in_use() / (1024 * 1024);
auto tmp_texture_memory_size = m_texture_cache.get_temporary_memory_in_use() / (1024 * 1024);
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 126, direct_fbo->width(), direct_fbo->height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 144, direct_fbo->width(), direct_fbo->height(), "Texture cache memory: " + std::to_string(texture_memory_size) + "M");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 162, direct_fbo->width(), direct_fbo->height(), "Temporary texture memory: " + std::to_string(tmp_texture_memory_size) + "M");
}
vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subres);
m_framebuffers_to_clean.push_back(std::move(direct_fbo));
@ -3088,4 +3212,10 @@ void VKGSRender::get_occlusion_query_result(rsx::occlusion_query_info* query)
m_occlusion_query_pool.reset_queries(*m_current_command_buffer, data.indices);
m_occlusion_map.erase(query->driver_handle);
}
void VKGSRender::shell_do_cleanup()
{
//TODO: Guard this
m_overlay_cleanup_requests.push_back(0);
}

View file

@ -136,6 +136,7 @@ private:
std::unique_ptr<vk::text_writer> m_text_writer;
std::unique_ptr<vk::depth_convert_pass> m_depth_converter;
std::unique_ptr<vk::ui_overlay_renderer> m_ui_renderer;
std::mutex m_sampler_mutex;
u64 surface_store_tag = 0;
@ -287,6 +288,8 @@ private:
//Vertex layout
rsx::vertex_input_layout m_vertex_layout;
std::vector<u64> m_overlay_cleanup_requests;
#if !defined(_WIN32) && defined(HAVE_VULKAN)
Display *m_display_handle = nullptr;
@ -341,10 +344,12 @@ protected:
bool do_method(u32 id, u32 arg) override;
void flip(int buffer) override;
void do_local_task() override;
void do_local_task(bool idle) override;
bool scaled_image_from_memory(rsx::blit_src_info& src, rsx::blit_dst_info& dst, bool interpolate) override;
void notify_tile_unbound(u32 tile) override;
bool on_access_violation(u32 address, bool is_writing) override;
void on_notify_memory_unmapped(u32 address_base, u32 size) override;
void shell_do_cleanup() override;
};

View file

@ -455,7 +455,7 @@ namespace vk
struct image_view
{
VkImageView value;
VkImageView value = VK_NULL_HANDLE;
VkImageViewCreateInfo info = {};
image_view(VkDevice dev, VkImage image, VkImageViewType view_type, VkFormat format, VkComponentMapping mapping, VkImageSubresourceRange range)
@ -477,6 +477,34 @@ namespace vk
CHECK_RESULT(vkCreateImageView(m_device, &info, nullptr, &value));
}
image_view(VkDevice dev, vk::image* resource, VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, VkComponentMapping mapping = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A })
: m_device(dev)
{
info.format = resource->info.format;
info.image = resource->value;
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.components = mapping;
info.subresourceRange = range;
switch (resource->info.imageType)
{
case VK_IMAGE_TYPE_1D:
info.viewType = VK_IMAGE_VIEW_TYPE_1D;
break;
case VK_IMAGE_TYPE_2D:
if (resource->info.flags == VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
else
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
break;
case VK_IMAGE_TYPE_3D:
info.viewType = VK_IMAGE_VIEW_TYPE_3D;
break;
}
CHECK_RESULT(vkCreateImageView(m_device, &info, nullptr, &value));
}
~image_view()
{
vkDestroyImageView(m_device, value, nullptr);
@ -1181,7 +1209,7 @@ namespace vk
m_instance = nullptr;
//Check that some critical entry-points have been loaded into memory indicating prescence of a loader
loader_exists = (&vkCreateInstance != nullptr);
loader_exists = (vkCreateInstance != nullptr);
}
~context()
@ -1279,8 +1307,8 @@ namespace vk
instance_info.pApplicationInfo = &app;
instance_info.enabledLayerCount = static_cast<uint32_t>(layers.size());
instance_info.ppEnabledLayerNames = layers.data();
instance_info.enabledExtensionCount = fast? 0: static_cast<uint32_t>(extensions.size());
instance_info.ppEnabledExtensionNames = fast? nullptr: extensions.data();
instance_info.enabledExtensionCount = fast ? 0 : static_cast<uint32_t>(extensions.size());
instance_info.ppEnabledExtensionNames = fast ? nullptr : extensions.data();
VkInstance instance;
if (vkCreateInstance(&instance_info, nullptr, &instance) != VK_SUCCESS)
@ -1704,5 +1732,5 @@ namespace vk
*/
void copy_mipmaped_image_using_buffer(VkCommandBuffer cmd, VkImage dst_image,
const std::vector<rsx_subresource_layout>& subresource_layout, int format, bool is_swizzled, u16 mipmap_count,
VkImageAspectFlags flags, vk::vk_data_heap &upload_heap, vk::buffer* upload_buffer);
VkImageAspectFlags flags, vk::vk_data_heap &upload_heap);
}

View file

@ -4,6 +4,8 @@
#include "VKFragmentProgram.h"
#include "VKRenderTargets.h"
#include "../overlays.h"
namespace vk
{
//TODO: Refactor text print class to inherit from this base class
@ -21,6 +23,8 @@ namespace vk
std::unordered_map<VkRenderPass, std::unique_ptr<vk::glsl::program>> m_program_cache;
std::unique_ptr<vk::sampler> m_sampler;
std::unique_ptr<vk::framebuffer> m_draw_fbo;
std::unique_ptr<vk::buffer> m_vao;
std::unique_ptr<vk::buffer> m_ubo;
vk::render_device* m_device = nullptr;
std::string vs_src;
@ -32,33 +36,43 @@ namespace vk
bool write_color = true;
bool write_depth = true;
bool no_depth_test = true;
bool enable_blend = false;
}
renderpass_config;
bool initialized = false;
bool compiled = false;
u32 num_drawable_elements = 4;
u32 first_vertex = 0;
void init_descriptors()
{
VkDescriptorPoolSize descriptor_pools[1] =
VkDescriptorPoolSize descriptor_pool_sizes[2] =
{
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 120 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 120 },
};
//Reserve descriptor pools
m_descriptor_pool.create(*m_device, descriptor_pools, 1);
m_descriptor_pool.create(*m_device, descriptor_pool_sizes, 2);
VkDescriptorSetLayoutBinding bindings[1] = {};
VkDescriptorSetLayoutBinding bindings[2] = {};
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[0].descriptorCount = 1;
bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[0].binding = 0;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[1].descriptorCount = 1;
bindings[1].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[1].binding = 1;
VkDescriptorSetLayoutCreateInfo infos = {};
infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
infos.pBindings = bindings;
infos.bindingCount = 1;
infos.bindingCount = 2;
CHECK_RESULT(vkCreateDescriptorSetLayout(*m_device, &infos, nullptr, &m_descriptor_layout));
@ -70,6 +84,45 @@ namespace vk
CHECK_RESULT(vkCreatePipelineLayout(*m_device, &layout_info, nullptr, &m_pipeline_layout));
}
virtual void update_uniforms(vk::glsl::program *program)
{
}
virtual std::vector<vk::glsl::program_input> get_vertex_inputs()
{
if (!m_vao)
{
auto memory_types = vk::get_memory_mapping(m_device->gpu());
m_vao = std::make_unique<vk::buffer>(*m_device, 1 * 0x100000, memory_types.host_visible_coherent, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 0);
m_ubo = std::make_unique<vk::buffer>(*m_device, 8 * 0x100000, memory_types.host_visible_coherent, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 0);
}
return{};
}
virtual std::vector<vk::glsl::program_input> get_fragment_inputs()
{
std::vector<vk::glsl::program_input> fs_inputs;
fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_texture,{},{}, 0, "fs0" });
fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_uniform_buffer,{},{}, 1, "static_data" });
return fs_inputs;
}
void upload_vertex_data(f32 *data, u32 first, u32 count)
{
verify(HERE), (first + count) <= 65536;
if (!m_vao)
{
auto memory_types = vk::get_memory_mapping(m_device->gpu());
m_vao = std::make_unique<vk::buffer>(*m_device, 1 * 0x100000, memory_types.host_visible_coherent, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 0);
m_ubo = std::make_unique<vk::buffer>(*m_device, 8 * 0x100000, memory_types.host_visible_coherent, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 0);
}
auto dst = m_vao->map((first * 4), VK_WHOLE_SIZE);
std::memcpy(dst, data, count * sizeof(f32));
m_vao->unmap();
}
vk::glsl::program* build_pipeline(VkRenderPass render_pass)
{
if (!compiled)
@ -101,8 +154,14 @@ namespace vk
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
dynamic_state_info.pDynamicStates = dynamic_state_descriptors;
VkVertexInputBindingDescription vb = { 0, 16, VK_VERTEX_INPUT_RATE_VERTEX };
VkVertexInputAttributeDescription via = { 0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0 };
VkPipelineVertexInputStateCreateInfo vi = {};
vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vi.vertexBindingDescriptionCount = 1;
vi.pVertexBindingDescriptions = &vb;
vi.vertexAttributeDescriptionCount = 1;
vi.pVertexAttributeDescriptions = &via;
VkPipelineViewportStateCreateInfo vp = {};
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@ -125,8 +184,19 @@ namespace vk
VkPipelineColorBlendAttachmentState att = {};
if (renderpass_config.write_color)
{
att.colorWriteMask = 0xf;
if (renderpass_config.enable_blend)
{
att.blendEnable = VK_TRUE;
att.alphaBlendOp = VK_BLEND_OP_ADD;
att.colorBlendOp = VK_BLEND_OP_ADD;
att.dstAlphaBlendFactor = att.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
att.srcAlphaBlendFactor = att.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
}
}
VkPipelineColorBlendStateCreateInfo cs = {};
cs.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
cs.attachmentCount = renderpass_config.color_attachments;
@ -158,17 +228,14 @@ namespace vk
CHECK_RESULT(vkCreateGraphicsPipelines(*m_device, nullptr, 1, &info, NULL, &pipeline));
const std::vector<vk::glsl::program_input> unused;
std::vector<vk::glsl::program_input> fs_inputs;
fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_texture, {}, {}, 0, "fs0"});
auto program = std::make_unique<vk::glsl::program>(*m_device, pipeline, unused, fs_inputs);
auto program = std::make_unique<vk::glsl::program>(*m_device, pipeline, get_vertex_inputs(), get_fragment_inputs());
auto result = program.get();
m_program_cache[render_pass] = std::move(program);
return result;
}
void load_program(vk::command_buffer cmd, VkRenderPass pass, vk::image_view *src)
void load_program(vk::command_buffer cmd, VkRenderPass pass, VkImageView src)
{
vk::glsl::program *program = nullptr;
auto found = m_program_cache.find(pass);
@ -195,11 +262,18 @@ namespace vk
VK_FALSE, 0.f, 1.f, 0.f, 0.f, VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
}
VkDescriptorImageInfo info = { m_sampler->value, src->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
VkDescriptorImageInfo info = { m_sampler->value, src, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
program->bind_uniform(info, "fs0", m_descriptor_set);
program->bind_uniform({ m_ubo->value, first_vertex * 128, 128 }, 1, m_descriptor_set);
update_uniforms(program);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, program->pipeline);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, 1, &m_descriptor_set, 0, nullptr);
VkBuffer buffers = m_vao->value;
VkDeviceSize offsets = 0;
vkCmdBindVertexBuffers(cmd, 0, 1, &buffers, &offsets);
}
void create(vk::render_device &dev)
@ -274,10 +348,13 @@ namespace vk
return result;
}
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, vk::image_view* src, VkRenderPass render_pass, std::list<std::unique_ptr<vk::framebuffer_holder>>& framebuffer_resources)
virtual void emit_geometry(vk::command_buffer &cmd)
{
vk::framebuffer *fbo = get_framebuffer(target, render_pass, framebuffer_resources);
vkCmdDraw(cmd, num_drawable_elements, 1, first_vertex, 0);
}
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::framebuffer* fbo, VkImageView src, VkRenderPass render_pass)
{
load_program(cmd, render_pass, src);
VkViewport vp{};
@ -300,9 +377,20 @@ namespace vk
rp_begin.renderArea.extent.height = h;
vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdDraw(cmd, 4, 1, 0, 0);
emit_geometry(cmd);
vkCmdEndRenderPass(cmd);
}
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, VkImageView src, VkRenderPass render_pass, std::list<std::unique_ptr<vk::framebuffer_holder>>& framebuffer_resources)
{
vk::framebuffer *fbo = get_framebuffer(target, render_pass, framebuffer_resources);
run(cmd, w, h, fbo, src, render_pass);
}
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, vk::image_view* src, VkRenderPass render_pass, std::list<std::unique_ptr<vk::framebuffer_holder>>& framebuffer_resources)
{
run(cmd, w, h, target, src->value, render_pass, framebuffer_resources);
}
};
struct depth_convert_pass : public overlay_pass
@ -344,4 +432,288 @@ namespace vk
m_fragment_shader.id = 100003;
}
};
struct ui_overlay_renderer : public overlay_pass
{
f32 m_time = 0.f;
color4f m_scale_offset;
color4f m_color;
bool m_pulse_glow = false;
bool m_skip_texture_read = false;
bool m_clip_enabled;
areaf m_clip_region;
std::vector<std::unique_ptr<vk::image>> resources;
std::unordered_map<u64, std::unique_ptr<vk::image>> font_cache;
std::unordered_map<u64, std::unique_ptr<vk::image_view>> view_cache;
std::vector<std::unique_ptr<vk::image>> temp_image_cache;
std::unordered_map<u64, std::unique_ptr<vk::image_view>> temp_view_cache;
ui_overlay_renderer()
{
vs_src =
{
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"layout(location=0) in vec4 in_pos;\n"
"layout(std140, set=0, binding=1) uniform static_data{ vec4 regs[8]; };\n"
"layout(location=0) out vec2 tc0;\n"
"layout(location=1) out vec4 color;\n"
"layout(location=2) out vec4 parameters;\n"
"layout(location=3) out vec4 clip_rect;\n"
"\n"
"void main()\n"
"{\n"
" tc0.xy = in_pos.zw;\n"
" color = regs[1];\n"
" parameters = regs[2];\n"
" clip_rect = regs[3] * regs[0].zwzw;\n"
" vec4 pos = vec4((in_pos.xy * regs[0].zw) / regs[0].xy, 0.5, 1.);\n"
" gl_Position = (pos + pos) - 1.;\n"
"}\n"
};
fs_src =
{
"#version 420\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"layout(set=0, binding=0) uniform sampler2D fs0;\n"
"layout(location=0) in vec2 tc0;\n"
"layout(location=1) in vec4 color;\n"
"layout(location=2) in vec4 parameters;\n"
"layout(location=3) in vec4 clip_rect;\n"
"layout(location=0) out vec4 ocol;\n"
"\n"
"void main()\n"
"{\n"
" if (parameters.w != 0)\n"
" {"
" if (gl_FragCoord.x < clip_rect.x || gl_FragCoord.x > clip_rect.z ||\n"
" gl_FragCoord.y < clip_rect.y || gl_FragCoord.y > clip_rect.w)\n"
" {\n"
" discard;\n"
" return;\n"
" }\n"
" }\n"
"\n"
" vec4 diff_color = color;\n"
" if (parameters.y != 0)\n"
" diff_color.a *= (sin(parameters.x) + 1.f) * 0.5f;\n"
"\n"
" if (parameters.z != 0)\n"
" ocol = texture(fs0, tc0) * diff_color;\n"
" else\n"
" ocol = diff_color;\n"
"}\n"
};
renderpass_config.color_attachments = 1;
renderpass_config.write_color = true;
renderpass_config.write_depth = false;
renderpass_config.enable_blend = true;
m_vertex_shader.id = 100004;
m_fragment_shader.id = 100005;
}
vk::image_view* upload_simple_texture(vk::render_device &dev, vk::command_buffer &cmd, vk::memory_type_mapping &memory_types,
vk::vk_data_heap& upload_heap, u64 key, int w, int h, bool font, bool temp, void *pixel_src)
{
const VkFormat format = (font) ? VK_FORMAT_R8_UNORM : VK_FORMAT_B8G8R8A8_UNORM;
const u32 pitch = (font) ? w : w * 4;
const u32 data_size = pitch * h;
const auto offset = upload_heap.alloc<512>(data_size);
const auto addr = upload_heap.map(offset, data_size);
const VkComponentMapping bgra = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A };
const VkComponentMapping rrrr = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R };
const VkComponentMapping mapping = (font) ? rrrr : bgra;
const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
auto tex = std::make_unique<vk::image>(dev, memory_types.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
VK_IMAGE_TYPE_2D, format, w, h, 1, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
0);
if (pixel_src && data_size)
std::memcpy(addr, pixel_src, data_size);
else if (data_size)
std::memset(addr, 0, data_size);
upload_heap.unmap();
VkBufferImageCopy region;
region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
region.bufferOffset = offset;
region.bufferRowLength = w;
region.bufferImageHeight = h;
region.imageOffset = {};
region.imageExtent = { (u32)w, (u32)h, 1u};
change_image_layout(cmd, tex.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range);
vkCmdCopyBufferToImage(cmd, upload_heap.heap->value, tex->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
change_image_layout(cmd, tex.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range);
auto view = std::make_unique<vk::image_view>(dev, tex.get(), range, mapping);
auto result = view.get();
if (!temp || font)
view_cache[key] = std::move(view);
else
temp_view_cache[key] = std::move(view);
if (font)
font_cache[key] = std::move(tex);
else if (!temp)
resources.push_back(std::move(tex));
else
temp_image_cache.push_back(std::move(tex));
return result;
}
void create(vk::command_buffer &cmd, vk::memory_type_mapping &memory_types, vk::vk_data_heap &upload_heap)
{
auto& dev = cmd.get_command_pool().get_owner();
overlay_pass::create(dev);
rsx::overlays::resource_config configuration;
configuration.load_files();
u64 storage_key = 1;
for (const auto &res : configuration.texture_raw_data)
{
upload_simple_texture(dev, cmd, memory_types, upload_heap, storage_key++, res->w, res->h, false, false, res->data);
}
configuration.free_resources();
}
void destroy()
{
temp_image_cache.clear();
temp_view_cache.clear();
resources.clear();
font_cache.clear();
view_cache.clear();
overlay_pass::destroy();
}
void remove_temp_resources()
{
temp_image_cache.clear();
temp_view_cache.clear();
}
vk::image_view* find_font(rsx::overlays::font *font, vk::command_buffer &cmd, vk::memory_type_mapping &memory_types, vk::vk_data_heap &upload_heap)
{
u64 key = (u64)font;
auto found = view_cache.find(key);
if (found != view_cache.end())
return found->second.get();
//Create font file
return upload_simple_texture(cmd.get_command_pool().get_owner(), cmd, memory_types, upload_heap, key, font->width, font->height, true, false, font->glyph_data.data());
}
vk::image_view* find_temp_image(rsx::overlays::image_info *desc, vk::command_buffer &cmd, vk::memory_type_mapping &memory_types, vk::vk_data_heap &upload_heap)
{
u64 key = (u64)desc;
auto found = temp_view_cache.find(key);
if (found != temp_view_cache.end())
return found->second.get();
return upload_simple_texture(cmd.get_command_pool().get_owner(), cmd, memory_types, upload_heap, key, desc->w, desc->h, false, true, desc->data);
}
void update_uniforms(vk::glsl::program *program) override
{
auto dst = (f32*)m_ubo->map(first_vertex * 128, 128);
dst[0] = m_scale_offset.r;
dst[1] = m_scale_offset.g;
dst[2] = m_scale_offset.b;
dst[3] = m_scale_offset.a;
dst[4] = m_color.r;
dst[5] = m_color.g;
dst[6] = m_color.b;
dst[7] = m_color.a;
dst[8] = m_time;
dst[9] = m_pulse_glow? 1.f : 0.f;
dst[10] = m_skip_texture_read? 0.f : 1.f;
dst[11] = m_clip_enabled ? 1.f : 0.f;
dst[12] = m_clip_region.x1;
dst[13] = m_clip_region.y1;
dst[14] = m_clip_region.x2;
dst[15] = m_clip_region.y2;
m_ubo->unmap();
}
void emit_geometry(vk::command_buffer &cmd)
{
//Split into groups of 4
auto tmp_first = first_vertex;
auto num_quads = num_drawable_elements / 4;
for (u32 n = 0; n < num_quads; ++n)
{
vkCmdDraw(cmd, 4, 1, tmp_first, 0);
tmp_first += 4;
}
}
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::framebuffer* target, VkRenderPass render_pass, vk::memory_type_mapping &memory_types,
vk::vk_data_heap &upload_heap, rsx::overlays::user_interface &ui)
{
m_scale_offset = color4f((f32)ui.virtual_width, (f32)ui.virtual_height, 1.f, 1.f);
m_time = (f32)(get_system_time() / 1000) * 0.005f;
u32 vertex_data_offset = 0;
first_vertex = 0;
for (auto &command : ui.get_compiled().draw_commands)
{
num_drawable_elements = (u32)command.second.size();
const u32 value_count = num_drawable_elements * 4;
upload_vertex_data((f32*)command.second.data(), vertex_data_offset, value_count);
m_skip_texture_read = false;
m_color = command.first.color;
m_pulse_glow = command.first.pulse_glow;
m_clip_enabled = command.first.clip_region;
m_clip_region = command.first.clip_rect;
auto src = vk::null_image_view(cmd);
switch (command.first.texture_ref)
{
case rsx::overlays::image_resource_id::game_icon:
case rsx::overlays::image_resource_id::backbuffer:
//TODO
case rsx::overlays::image_resource_id::none:
m_skip_texture_read = true;
break;
case rsx::overlays::image_resource_id::font_file:
src = find_font(command.first.font_ref, cmd, memory_types, upload_heap)->value;
break;
case rsx::overlays::image_resource_id::raw_image:
src = find_temp_image((rsx::overlays::image_info*)command.first.external_data_ref, cmd, memory_types, upload_heap)->value;
break;
default:
src = view_cache[command.first.texture_ref]->value;
break;
}
overlay_pass::run(cmd, w, h, target, src, render_pass);
vertex_data_offset += value_count;
first_vertex += num_drawable_elements;
}
ui.update();
}
};
}

View file

@ -149,7 +149,7 @@ namespace vk
void copy_mipmaped_image_using_buffer(VkCommandBuffer cmd, VkImage dst_image,
const std::vector<rsx_subresource_layout>& subresource_layout, int format, bool is_swizzled, u16 mipmap_count,
VkImageAspectFlags flags, vk::vk_data_heap &upload_heap, vk::buffer* upload_buffer)
VkImageAspectFlags flags, vk::vk_data_heap &upload_heap)
{
u32 mipmap_level = 0;
u32 block_in_pixel = get_format_block_size_in_texel(format);
@ -160,10 +160,10 @@ namespace vk
u32 image_linear_size = row_pitch * layout.height_in_block * layout.depth;
size_t offset_in_buffer = upload_heap.alloc<512>(image_linear_size);
void *mapped_buffer = upload_buffer->map(offset_in_buffer, image_linear_size);
void *mapped_buffer = upload_heap.map(offset_in_buffer, image_linear_size);
gsl::span<gsl::byte> mapped{ (gsl::byte*)mapped_buffer, ::narrow<int>(image_linear_size) };
upload_texture_subresource(mapped, layout, format, is_swizzled, 256);
upload_buffer->unmap();
upload_heap.unmap();
VkBufferImageCopy copy_info = {};
copy_info.bufferOffset = offset_in_buffer;
@ -176,7 +176,7 @@ namespace vk
copy_info.imageSubresource.mipLevel = mipmap_level % mipmap_count;
copy_info.bufferRowLength = block_in_pixel * row_pitch / block_size_in_bytes;
vkCmdCopyBufferToImage(cmd, upload_buffer->value, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_info);
vkCmdCopyBufferToImage(cmd, upload_heap.heap->value, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_info);
mipmap_level++;
}
}

View file

@ -425,7 +425,6 @@ namespace vk
vk::gpu_formats_support m_formats_support;
VkQueue m_submit_queue;
vk_data_heap* m_texture_upload_heap;
vk::buffer* m_texture_upload_buffer;
//Stuff that has been dereferenced goes into these
std::list<discarded_storage> m_discardable_storage;
@ -787,7 +786,7 @@ namespace vk
}
vk::copy_mipmaped_image_using_buffer(cmd, image->value, subresource_layout, gcm_format, input_swizzled, mipmaps, subres_range.aspectMask,
*m_texture_upload_heap, m_texture_upload_buffer);
*m_texture_upload_heap);
vk::leave_uninterruptible();
@ -848,14 +847,13 @@ namespace vk
public:
void initialize(vk::render_device& device, vk::memory_type_mapping& memory_types, vk::gpu_formats_support& formats_support,
VkQueue submit_queue, vk::vk_data_heap& upload_heap, vk::buffer* upload_buffer)
VkQueue submit_queue, vk::vk_data_heap& upload_heap)
{
m_memory_types = memory_types;
m_formats_support = formats_support;
m_device = &device;
m_submit_queue = submit_queue;
m_texture_upload_heap = &upload_heap;
m_texture_upload_buffer = upload_buffer;
}
void destroy() override

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
#include "stdafx.h"
#include "overlays.h"
#include "GSRender.h"
namespace rsx
{
namespace overlays
{
//Singleton instance declaration
fontmgr* fontmgr::m_instance = nullptr;
void user_interface::close()
{
//Force unload
exit = true;
if (auto rsxthr = fxm::get<GSRender>())
rsxthr->shell_close_dialog();
if (on_close)
on_close(return_code);
}
void user_interface::refresh()
{
if (auto rsxthr = fxm::get<GSRender>())
{
rsxthr->native_ui_flip_request.store(true);
}
}
}
}

762
rpcs3/Emu/RSX/overlays.h Normal file
View file

@ -0,0 +1,762 @@
#pragma once
#include "overlay_controls.h"
#include "../../Utilities/Thread.h"
#include "../Io/PadHandler.h"
#include "Emu/Memory/vm.h"
#include "Emu/IdManager.h"
#include "pad_thread.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Cell/Modules/cellSaveData.h"
#include "Emu/Cell/Modules/cellMsgDialog.h"
#include "Emu/Cell/Modules/sceNpTrophy.h"
#include <time.h>
extern u64 get_system_time();
// Definition of user interface implementations
namespace rsx
{
namespace overlays
{
struct user_interface
{
//Move this somewhere to avoid duplication
enum selection_code
{
new_save = -1,
canceled = -2,
error = -3
};
enum pad_button : u8
{
dpad_up = 0,
dpad_down,
dpad_left,
dpad_right,
triangle,
circle,
square,
cross
};
u16 virtual_width = 1280;
u16 virtual_height = 720;
u64 input_timestamp = 0;
bool exit = false;
s32 return_code = CELL_OK;
std::function<void(s32 status)> on_close;
virtual compiled_resource get_compiled() = 0;
void close();
void refresh();
virtual void update(){}
virtual void on_button_pressed(pad_button button_press)
{
close();
};
s32 run_input_loop()
{
const auto handler = fxm::get<pad_thread>();
if (!handler)
{
LOG_ERROR(RSX, "Pad handler expected but none initialized!");
return selection_code::error;
}
const PadInfo& rinfo = handler->GetInfo();
if (rinfo.max_connect == 0 || !rinfo.now_connect)
return selection_code::error;
std::array<bool, 8> button_state;
button_state.fill(true);
while (!exit)
{
if (Emu.IsStopped())
return selection_code::canceled;
if (Emu.IsPaused())
{
std::this_thread::sleep_for(10ms);
continue;
}
for (const auto &pad : handler->GetPads())
{
for (auto &button : pad->m_buttons)
{
u8 button_id = 255;
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
{
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_LEFT:
button_id = pad_button::dpad_left;
break;
case CELL_PAD_CTRL_RIGHT:
button_id = pad_button::dpad_right;
break;
case CELL_PAD_CTRL_DOWN:
button_id = pad_button::dpad_down;
break;
case CELL_PAD_CTRL_UP:
button_id = pad_button::dpad_up;
break;
}
}
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
{
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_TRIANGLE:
button_id = pad_button::triangle;
break;
case CELL_PAD_CTRL_CIRCLE:
button_id = pad_button::circle;
break;
case CELL_PAD_CTRL_SQUARE:
button_id = pad_button::square;
break;
case CELL_PAD_CTRL_CROSS:
button_id = pad_button::cross;
break;
}
}
if (button_id < 255)
{
if (button.m_pressed != button_state[button_id])
if (button.m_pressed) on_button_pressed(static_cast<pad_button>(button_id));
button_state[button_id] = button.m_pressed;
}
if (button.m_flush)
{
button.m_pressed = false;
button.m_flush = false;
button.m_value = 0;
}
if (exit)
return 0;
}
}
refresh();
}
//Unreachable
return 0;
}
};
struct fps_display : user_interface
{
label m_display;
fps_display()
{
m_display.w = 150;
m_display.h = 30;
m_display.set_font("Arial", 16);
m_display.set_pos(1100, 20);
}
void update(std::string current_fps)
{
m_display.text = current_fps;
m_display.refresh();
}
compiled_resource get_compiled() override
{
return m_display.get_compiled();
}
};
struct save_dialog : public user_interface
{
private:
struct save_dialog_entry : horizontal_layout
{
private:
std::unique_ptr<image_info> icon_data;
public:
save_dialog_entry(const char* text1, const char* text2, u8 resource_id, const std::vector<u8>& icon_buf)
{
std::unique_ptr<overlay_element> image = std::make_unique<image_view>();
image->set_size(160, 110);
image->set_padding(36.f, 36.f, 11.f, 11.f); //Square image, 88x88
if (resource_id != image_resource_id::raw_image)
{
static_cast<image_view*>(image.get())->set_image_resource(resource_id);
}
else if (icon_buf.size())
{
image->set_padding(0.f, 0.f, 11.f, 11.f); //Half sized icon, 320x176->160x88
icon_data = std::make_unique<image_info>(icon_buf);
static_cast<image_view*>(image.get())->set_raw_image(icon_data.get());
}
else
{
//Fallback
static_cast<image_view*>(image.get())->set_image_resource(resource_config::standard_image_resource::save);
}
std::unique_ptr<overlay_element> text_stack = std::make_unique<vertical_layout>();
std::unique_ptr<overlay_element> padding = std::make_unique<spacer>();
std::unique_ptr<overlay_element> header_text = std::make_unique<label>(text1);
std::unique_ptr<overlay_element> subtext = std::make_unique<label>(text2);
padding->set_size(1, 10);
header_text->set_size(800, 40);
header_text->text = text1;
header_text->set_font("Arial", 16);
header_text->set_wrap_text(true);
subtext->set_size(800, 40);
subtext->text = text2;
subtext->set_font("Arial", 14);
subtext->set_wrap_text(true);
//Auto-resize save details label
static_cast<label*>(subtext.get())->auto_resize(true);
//Make back color transparent for text
header_text->back_color.a = 0.f;
subtext->back_color.a = 0.f;
static_cast<vertical_layout*>(text_stack.get())->pack_padding = 5;
static_cast<vertical_layout*>(text_stack.get())->add_element(padding);
static_cast<vertical_layout*>(text_stack.get())->add_element(header_text);
static_cast<vertical_layout*>(text_stack.get())->add_element(subtext);
//Pack
this->pack_padding = 15;
add_element(image);
add_element(text_stack);
}
};
std::unique_ptr<overlay_element> m_dim_background;
std::unique_ptr<list_view> m_list;
std::unique_ptr<label> m_description;
std::unique_ptr<label> m_time_thingy;
std::string current_time()
{
time_t t = time(NULL);
char buf[128];
strftime(buf, 128, "%c", localtime(&t));
return buf;
}
public:
save_dialog()
{
m_dim_background = std::make_unique<overlay_element>();
m_dim_background->set_size(1280, 720);
m_list = std::make_unique<list_view>(1240, 540);
m_description = std::make_unique<label>();
m_time_thingy = std::make_unique<label>();
m_list->set_pos(20, 85);
m_description->set_font("Arial", 20);
m_description->set_pos(20, 50);
m_description->text = "Save Dialog";
m_time_thingy->set_font("Arial", 14);
m_time_thingy->set_pos(1000, 40);
m_time_thingy->text = current_time();
static_cast<label*>(m_description.get())->auto_resize();
static_cast<label*>(m_time_thingy.get())->auto_resize();
m_dim_background->back_color.a = 0.8f;
return_code = selection_code::canceled;
}
void on_button_pressed(pad_button button_press) override
{
switch (button_press)
{
case pad_button::cross:
return_code = m_list->get_selected_index();
//Fall through
case pad_button::circle:
close();
break;
case pad_button::dpad_up:
m_list->select_previous();
break;
case pad_button::dpad_down:
m_list->select_next();
break;
default:
LOG_TRACE(RSX, "[ui] Button %d pressed", (u8)button_press);
}
}
compiled_resource get_compiled() override
{
compiled_resource result;
result.add(m_dim_background->get_compiled());
result.add(m_list->get_compiled());
result.add(m_description->get_compiled());
result.add(m_time_thingy->get_compiled());
return result;
}
s32 show(std::vector<SaveDataEntry>& save_entries, u32 op, vm::ptr<CellSaveDataListSet> listSet)
{
std::vector<u8> null_icon;
auto num_actual_saves = save_entries.size();
for (auto &entry : save_entries)
{
std::unique_ptr<overlay_element> e = std::make_unique<save_dialog_entry>(entry.title.c_str(), (entry.subtitle + " - " + entry.details).c_str(), image_resource_id::raw_image, entry.iconBuf);
m_list->add_entry(e);
}
if (op >= 8)
{
m_description->text = "Delete Save";
}
else if (op & 1)
{
m_description->text = "Load Save";
}
else
{
m_description->text = "Create Save";
std::unique_ptr<overlay_element> new_stub = std::make_unique<save_dialog_entry>("Create New", "Select to create a new entry", resource_config::standard_image_resource::new_entry, null_icon);
m_list->add_entry(new_stub);
}
static_cast<label*>(m_description.get())->auto_resize();
if (auto err = run_input_loop())
return err;
if (return_code == num_actual_saves)
return selection_code::new_save;
return return_code;
}
void update() override
{
m_time_thingy->set_text(current_time());
static_cast<label*>(m_time_thingy.get())->auto_resize();
}
};
struct message_dialog : public user_interface
{
private:
label text_display;
image_button btn_ok;
image_button btn_cancel;
overlay_element bottom_bar, background;
progress_bar progress_1, progress_2;
u8 num_progress_bars = 0;
bool interactive = false;
bool ok_only = false;
bool cancel_only = false;
public:
message_dialog()
{
background.set_size(1280, 720);
background.back_color.a = 0.85f;
text_display.set_size(1100, 40);
text_display.set_pos(90, 375);
text_display.set_font("Arial", 16);
text_display.align_text(overlay_element::text_align::center);
text_display.set_wrap_text(true);
bottom_bar.back_color = color4f(1.f, 1.f, 1.f, 1.f);
bottom_bar.set_size(1200, 2);
bottom_bar.set_pos(40, 400);
progress_1.set_size(800, 4);
progress_2.set_size(800, 4);
progress_1.back_color = color4f(0.25f, 0.f, 0.f, 0.85f);
progress_2.back_color = color4f(0.25f, 0.f, 0.f, 0.85f);
btn_ok.set_image_resource(resource_config::standard_image_resource::cross);
btn_ok.set_text("Yes");
btn_ok.set_size(140, 30);
btn_ok.set_pos(545, 420);
btn_ok.set_font("Arial", 16);
btn_cancel.set_image_resource(resource_config::standard_image_resource::circle);
btn_cancel.set_text("No");
btn_cancel.set_size(140, 30);
btn_cancel.set_pos(685, 420);
btn_cancel.set_font("Arial", 16);
return_code = CELL_MSGDIALOG_BUTTON_NONE;
}
compiled_resource get_compiled() override
{
compiled_resource result;
result.add(background.get_compiled());
result.add(text_display.get_compiled());
if (num_progress_bars > 0)
result.add(progress_1.get_compiled());
if (num_progress_bars > 1)
result.add(progress_2.get_compiled());
if (interactive)
{
if (!num_progress_bars)
result.add(bottom_bar.get_compiled());
if (!cancel_only)
result.add(btn_ok.get_compiled());
if (!ok_only)
result.add(btn_cancel.get_compiled());
}
return result;
}
void on_button_pressed(pad_button button_press) override
{
switch (button_press)
{
case pad_button::cross:
{
if (ok_only)
{
return_code = CELL_MSGDIALOG_BUTTON_OK;
}
else if (cancel_only)
{
//Do not accept for cancel-only dialogs
return;
}
else
{
return_code = CELL_MSGDIALOG_BUTTON_YES;
}
break;
}
case pad_button::circle:
{
if (ok_only)
{
//Ignore cancel operation for Ok-only
return;
}
else if (cancel_only)
{
return_code = CELL_MSGDIALOG_BUTTON_ESCAPE;
}
else
{
return_code = CELL_MSGDIALOG_BUTTON_NO;
}
break;
}
default:
return;
}
close();
}
s32 show(std::string text, const MsgDialogType &type, std::function<void(s32 status)> on_close)
{
num_progress_bars = type.progress_bar_count;
if (num_progress_bars)
{
u16 offset = 50;
progress_1.set_pos(240, 420);
if (num_progress_bars > 1)
{
progress_2.set_pos(240, 470);
offset = 90;
}
//Push the other stuff down
bottom_bar.translate(0, offset);
btn_ok.translate(0, offset);
btn_cancel.translate(0, offset);
}
text_display.set_text(text.c_str());
u16 text_w, text_h;
text_display.measure_text(text_w, text_h);
text_display.translate(0, -(text_h - 16));
switch (type.button_type.unshifted())
{
case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_NONE:
interactive = !type.disable_cancel;
if (interactive)
{
btn_cancel.set_pos(585, btn_cancel.y);
btn_cancel.set_text("Cancel");
cancel_only = true;
}
break;
case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_OK:
btn_ok.set_pos(600, btn_ok.y);
btn_ok.set_text("OK");
interactive = true;
ok_only = true;
break;
case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO:
interactive = true;
break;
}
this->on_close = on_close;
if (interactive)
{
thread_ctrl::spawn("dialog input thread", [&]
{
if (auto error = run_input_loop())
{
LOG_ERROR(RSX, "Dialog input loop exited with error code=%d", error);
}
});
}
return CELL_OK;
}
s32 progress_bar_set_message(u32 index, const std::string& msg)
{
if (index >= num_progress_bars)
return CELL_MSGDIALOG_ERROR_PARAM;
if (index == 0)
progress_1.set_text(msg);
else
progress_2.set_text(msg);
return CELL_OK;
}
s32 progress_bar_increment(u32 index, f32 value)
{
if (index >= num_progress_bars)
return CELL_MSGDIALOG_ERROR_PARAM;
if (index == 0)
progress_1.inc(value);
else
progress_2.inc(value);
return CELL_OK;
}
s32 progress_bar_reset(u32 index)
{
if (index >= num_progress_bars)
return CELL_MSGDIALOG_ERROR_PARAM;
if (index == 0)
progress_1.set_value(0.f);
else
progress_2.set_value(0.f);
return CELL_OK;
}
};
struct trophy_notification : public user_interface
{
private:
overlay_element frame;
image_view image;
label text_view;
u64 creation_time = 0;
std::unique_ptr<image_info> icon_info;
public:
trophy_notification()
{
frame.set_pos(0, 0);
frame.set_size(260, 80);
frame.back_color.a = 0.85f;
image.set_pos(8, 8);
image.set_size(64, 64);
image.back_color.a = 0.f;
text_view.set_pos(85, 0);
text_view.set_padding(0.f, 0.f, 30.f, 0.f);
text_view.set_font("Arial", 8);
text_view.align_text(overlay_element::text_align::center);
text_view.back_color.a = 0.f;
}
void update() override
{
u64 t = get_system_time();
if (((t - creation_time) / 1000) > 7500)
close();
}
compiled_resource get_compiled() override
{
auto result = frame.get_compiled();
result.add(image.get_compiled());
result.add(text_view.get_compiled());
return result;
}
s32 show(const SceNpTrophyDetails& trophy, const std::vector<uchar>& trophy_icon_buffer)
{
if (trophy_icon_buffer.size())
{
icon_info = std::make_unique<image_info>(trophy_icon_buffer);
image.set_raw_image(icon_info.get());
}
std::string trophy_message;
switch (trophy.trophyGrade)
{
case SCE_NP_TROPHY_GRADE_BRONZE: trophy_message = "bronze"; break;
case SCE_NP_TROPHY_GRADE_SILVER: trophy_message = "silver"; break;
case SCE_NP_TROPHY_GRADE_GOLD: trophy_message = "gold"; break;
case SCE_NP_TROPHY_GRADE_PLATINUM: trophy_message = "platinum"; break;
default: break;
}
trophy_message = "You have earned the " + trophy_message + " trophy\n" + trophy.name;
text_view.set_text(trophy_message);
text_view.auto_resize();
//Resize background to cover the text
u16 margin_sz = text_view.x - image.w - image.x;
frame.w = text_view.x + text_view.w + margin_sz;
creation_time = get_system_time();
return CELL_OK;
}
};
struct shader_compile_notification : user_interface
{
label m_text;
overlay_element dots[3];
u8 current_dot = 255;
u64 creation_time = 0;
u64 expire_time = 0; //Time to end the prompt
u64 urgency_ctr = 0; //How critical it is to show to the user
shader_compile_notification()
{
m_text.set_font("Arial", 16);
m_text.set_text("Compiling shaders");
m_text.auto_resize();
m_text.set_pos(20, 700);
m_text.back_color.a = 0.f;
for (int n = 0; n < 3; ++n)
{
dots[n].set_size(2, 2);
dots[n].back_color = color4f(1.f, 1.f, 1.f, 1.f);
dots[n].set_pos( m_text.w + 25 + (6 * n), 710);
}
creation_time = get_system_time();
expire_time = creation_time + 1000000;
}
void update_animation(u64 t)
{
//Update rate is twice per second
auto elapsed = t - creation_time;
elapsed /= 500000;
auto old_dot = current_dot;
current_dot = elapsed % 3;
if (old_dot != current_dot)
{
if (old_dot != 255)
{
dots[old_dot].set_size(2, 2);
dots[old_dot].translate(0, 1);
}
dots[current_dot].translate(0, -1);
dots[current_dot].set_size(3, 3);
}
}
//Extends visible time by half a second. Also updates the screen
void touch()
{
if (urgency_ctr == 0 || urgency_ctr > 8)
{
refresh();
urgency_ctr = 0;
}
expire_time = get_system_time() + 500000;
urgency_ctr++;
}
void update() override
{
auto current_time = get_system_time();
if (current_time > expire_time)
close();
update_animation(current_time);
//Usually this method is called during a draw-to-screen operation. Reset urgency ctr
urgency_ctr = 1;
}
compiled_resource get_compiled() override
{
auto compiled = m_text.get_compiled();
compiled.add(dots[0].get_compiled());
compiled.add(dots[1].get_compiled());
compiled.add(dots[2].get_compiled());
return compiled;
}
};
}
}

View file

@ -279,6 +279,50 @@ namespace rsx
public:
struct progress_dialog_helper
{
std::shared_ptr<MsgDialogBase> dlg;
virtual void create()
{
dlg = Emu.GetCallbacks().get_msg_dialog();
dlg->type.se_normal = true;
dlg->type.bg_invisible = true;
dlg->type.progress_bar_count = 1;
dlg->on_close = [](s32 status)
{
Emu.CallAfter([]()
{
Emu.Stop();
});
};
Emu.CallAfter([&]()
{
dlg->Create("Preloading cached shaders from disk.\nPlease wait...");
});
}
virtual void update_msg(u32 processed, u32 entry_count)
{
Emu.CallAfter([=]()
{
dlg->ProgressBarSetMsg(0, fmt::format("Loading pipeline object %u of %u", processed, entry_count));
});
}
virtual void inc_value(u32 value)
{
Emu.CallAfter([=]()
{
dlg->ProgressBarInc(0, value);
});
}
virtual void close()
{}
};
shaders_cache(backend_storage& storage, std::string pipeline_class, std::string version_prefix_str = "v1")
: version_prefix(version_prefix_str)
, pipeline_class_name(pipeline_class)
@ -288,7 +332,7 @@ namespace rsx
}
template <typename... Args>
void load(Args&& ...args)
void load(progress_dialog_helper* dlg, Args&& ...args)
{
if (g_cfg.video.disable_on_disk_shader_cache)
{
@ -321,22 +365,14 @@ namespace rsx
root.rewind();
// Progress dialog
auto dlg = Emu.GetCallbacks().get_msg_dialog();
dlg->type.se_normal = true;
dlg->type.bg_invisible = true;
dlg->type.progress_bar_count = 1;
dlg->on_close = [](s32 status)
std::unique_ptr<progress_dialog_helper> fallback_dlg;
if (!dlg)
{
Emu.CallAfter([]()
{
Emu.Stop();
});
};
fallback_dlg = std::make_unique<progress_dialog_helper>();
dlg = fallback_dlg.get();
}
Emu.CallAfter([=]()
{
dlg->Create("Preloading cached shaders from disk.\nPlease wait...");
});
dlg->create();
const auto prefix_length = version_prefix.length();
u32 processed = 0;
@ -352,10 +388,7 @@ namespace rsx
fs::file f(directory_path + "/" + tmp.name);
processed++;
Emu.CallAfter([=]()
{
dlg->ProgressBarSetMsg(0, fmt::format("Loading pipeline object %u of %u", processed, entry_count));
});
dlg->update_msg(processed, entry_count);
if (f.size() != sizeof(pipeline_data))
{
@ -371,14 +404,13 @@ namespace rsx
if (tally > 1.f)
{
u32 value = (u32)tally;
Emu.CallAfter([=]()
{
dlg->ProgressBarInc(0, value);
});
dlg->inc_value(value);
tally -= (f32)value;
}
}
dlg->close();
}
void store(pipeline_storage_type &pipeline, RSXVertexProgram &vp, RSXFragmentProgram &fp)

View file

@ -3,6 +3,7 @@
#include "rsx_methods.h"
#include "Emu/RSX/GCM.h"
#include "Common/BufferUtils.h"
#include "overlays.h"
extern "C"
{

View file

@ -71,7 +71,7 @@ namespace rsx
{
if (x <= 2) return x;
return (1ULL << 32) >> ::cntlz32(x - 1, true);
return static_cast<u32>((1ULL << 32) >> ::cntlz32(x - 1, true));
}
/* Note: What the ps3 calls swizzling in this case is actually z-ordering / morton ordering of pixels

View file

@ -422,6 +422,7 @@ struct cfg_root : cfg::node
cfg::_bool start_fullscreen{ this, "Start games in fullscreen mode" };
cfg::_bool show_fps_in_title{ this, "Show FPS counter in window title", true};
cfg::_bool show_trophy_popups{ this, "Show trophy popups", true};
cfg::_bool use_native_interface{ this, "Use native user interface", true };
cfg::_int<1, 65535> gdb_server_port{this, "Port", 2345};
} misc{this};

View file

@ -63,7 +63,8 @@
"showFPSInTitle": "Shows the framerate in the game window title. May cause buggy or outdated recording software to not notice RPCS3.",
"gs_resizeOnBoot": "Automatically resizes the game window on boot.\nThis does not change the internal game resolution.",
"showTrophyPopups": "Show trophy popups when a trophy is unlocked.",
"gs_disableMouse": "Disables the activation of fullscreen mode per doubleclick while the game screen is active.\nCheck this if you want to play with mouse and keyboard (for example with UCR)."
"gs_disableMouse": "Disables the activation of fullscreen mode per doubleclick while the game screen is active.\nCheck this if you want to play with mouse and keyboard (for example with UCR).",
"useNativeInterface": "Enables use of native HUD within the game window that can interacts with the game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nNot all languages are currently supported (English only)"
}
},
"gpu": {

View file

@ -295,6 +295,7 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Emu\RSX\Null\NullGSRender.cpp" />
<ClCompile Include="Emu\RSX\overlays.cpp" />
<ClCompile Include="Emu\RSX\rsx_methods.cpp" />
<ClCompile Include="Emu\RSX\rsx_utils.cpp" />
<ClCompile Include="Crypto\aes.cpp">
@ -653,6 +654,8 @@
<ClInclude Include="Emu\RSX\Common\texture_cache.h" />
<ClInclude Include="Emu\RSX\gcm_enums.h" />
<ClInclude Include="Emu\RSX\gcm_printing.h" />
<ClInclude Include="Emu\RSX\overlays.h" />
<ClInclude Include="Emu\RSX\overlay_controls.h" />
<ClInclude Include="Emu\RSX\rsx_cache.h" />
<ClInclude Include="Emu\RSX\rsx_decode.h" />
<ClInclude Include="Emu\RSX\rsx_trace.h" />

View file

@ -934,9 +934,12 @@
</ClCompile>
<ClCompile Include="Emu\Cell\lv2\sys_net.cpp">
<Filter>Emu\Cell\lv2</Filter>
</ClCompile>
</ClCompile>
<ClCompile Include="Emu\Io\PadHandler.cpp">
<Filter>Emu\Io</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\overlays.cpp">
<Filter>Emu\GPU\RSX</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
@ -1810,5 +1813,11 @@
<ClInclude Include="Emu\Cell\lv2\sys_net.h">
<Filter>Emu\Cell\lv2</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\overlay_controls.h">
<Filter>Emu\GPU\RSX</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\overlays.h">
<Filter>Emu\GPU\RSX</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -85,6 +85,7 @@ public:
ShowFPSInTitle,
ShowTrophyPopups,
ShowWelcomeScreen,
UseNativeInterface,
// Network
ConnectionStatus,
@ -241,6 +242,7 @@ private:
{ShowFPSInTitle, { "Miscellaneous", "Show FPS counter in window title"}},
{ShowTrophyPopups, { "Miscellaneous", "Show trophy popups"}},
{ShowWelcomeScreen, { "Miscellaneous", "Show Welcome Screen"}},
{UseNativeInterface, { "Miscellaneous", "Use native user interface"}},
// Networking
{ConnectionStatus, { "Net", "Connection status"}},

View file

@ -1,9 +1,38 @@
#include "save_data_dialog.h"
#include "save_data_list_dialog.h"
#include <Emu/IdManager.h>
#include <Emu/RSX/GSRender.h>
s32 save_data_dialog::ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet)
{
save_data_list_dialog sdid(save_entries, focused, op, listSet);
sdid.exec();
return sdid.GetSelection();
//TODO: Install native shell as an Emu callback
if (auto rsxthr = fxm::get<GSRender>())
{
if (auto native_dlg = rsxthr->shell_open_save_dialog())
{
auto result = native_dlg->show(save_entries, op, listSet);
if (result != rsx::overlays::user_interface::selection_code::error)
return result;
}
}
//Fall back to front-end GUI
atomic_t<bool> dlg_result(false);
atomic_t<s32> selection;
Emu.CallAfter([&]()
{
save_data_list_dialog sdid(save_entries, focused, op, listSet);
sdid.exec();
selection = sdid.GetSelection();
dlg_result = true;
});
while (!dlg_result)
{
thread_ctrl::wait_for(1000);
}
return selection.load();
}

View file

@ -721,6 +721,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std:
xemu_settings->EnhanceCheckBox(ui->showTrophyPopups, emu_settings::ShowTrophyPopups);
SubscribeTooltip(ui->showTrophyPopups, json_emu_misc["showTrophyPopups"].toString());
xemu_settings->EnhanceCheckBox(ui->useNativeInterface, emu_settings::UseNativeInterface);
SubscribeTooltip(ui->useNativeInterface, json_emu_misc["useNativeInterface"].toString());
if (game)
{
ui->gb_stylesheets->setEnabled(false);

View file

@ -36,7 +36,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>7</number>
<number>6</number>
</property>
<widget class="QWidget" name="coreTab">
<attribute name="title">
@ -1270,6 +1270,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useNativeInterface">
<property name="text">
<string>Use native user interface</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View file

@ -2,13 +2,27 @@
#include "trophy_notification_frame.h"
#include "../Emu/System.h"
#include "../Emu/RSX/GSRender.h"
s32 trophy_notification_helper::ShowTrophyNotification(const SceNpTrophyDetails& trophy, const std::vector<uchar>& trophy_icon_buffer)
{
trophy_notification_frame* trophy_notification = new trophy_notification_frame(trophy_icon_buffer, trophy, m_game_window->frameGeometry().height()/10);
if (auto rsxthr = fxm::get<GSRender>())
{
if (auto dlg = rsxthr->shell_open_trophy_notification())
{
return dlg->show(trophy, trophy_icon_buffer);
}
}
// Move notification to upper lefthand corner
trophy_notification->move(m_game_window->mapToGlobal(QPoint(0, 0)));
trophy_notification->show();
Emu.CallAfter([=]
{
trophy_notification_frame* trophy_notification = new trophy_notification_frame(trophy_icon_buffer, trophy, m_game_window->frameGeometry().height() / 10);
// Move notification to upper lefthand corner
trophy_notification->move(m_game_window->mapToGlobal(QPoint(0, 0)));
trophy_notification->show();
});
return 0;
}