rsx/overlays: Introduce 'native' HUD UI and implement some common dialogs (#4011)
BIN
bin/Icons/ui/L1.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
bin/Icons/ui/L2.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
bin/Icons/ui/R1.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
bin/Icons/ui/R2.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
bin/Icons/ui/circle.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
bin/Icons/ui/cross.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
bin/Icons/ui/dpad.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
bin/Icons/ui/dpad_down.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
bin/Icons/ui/dpad_left.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
bin/Icons/ui/dpad_right.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
bin/Icons/ui/dpad_up.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
bin/Icons/ui/fade_bottom.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
bin/Icons/ui/fade_top.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
bin/Icons/ui/left_stick.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
bin/Icons/ui/new.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
bin/Icons/ui/right_stick.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
bin/Icons/ui/save.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
bin/Icons/ui/select.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
bin/Icons/ui/square.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
bin/Icons/ui/start.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
bin/Icons/ui/triangle.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
// UI returns -1 for new save games
|
||||
if (selected == -1)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,15 +754,70 @@ 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_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()
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -1416,3 +1500,8 @@ void GLGSRender::get_occlusion_query_result(rsx::occlusion_query_info* query)
|
|||
|
||||
query->result += result;
|
||||
}
|
||||
|
||||
void GLGSRender::shell_do_cleanup()
|
||||
{
|
||||
m_ui_renderer.remove_temp_resources();
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,6 +118,15 @@ namespace gl
|
|||
GLboolean color_writes[4];
|
||||
GLboolean depth_write;
|
||||
|
||||
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 (target_texture)
|
||||
{
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo.id());
|
||||
|
||||
|
@ -95,8 +141,9 @@ namespace gl
|
|||
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 (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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -142,7 +142,6 @@ namespace gl
|
|||
|
||||
switch (fmt_)
|
||||
{
|
||||
case texture::format::red:
|
||||
case texture::format::r:
|
||||
break;
|
||||
case texture::format::rg:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(){}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,14 +1606,64 @@ void VKGSRender::on_init_thread()
|
|||
GSRender::on_init_thread();
|
||||
rsx_thread = std::this_thread::get_id();
|
||||
|
||||
if (!supports_native_ui)
|
||||
{
|
||||
m_frame->disable_wm_event_queue();
|
||||
m_frame->hide();
|
||||
|
||||
m_shaders_cache->load(*m_device, pipeline_layout);
|
||||
|
||||
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;
|
||||
|
||||
native_helper(VKGSRender *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);
|
||||
|
||||
//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,6 +3082,13 @@ 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)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -2979,6 +3102,7 @@ void VKGSRender::flip(int buffer)
|
|||
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));
|
||||
|
@ -3089,3 +3213,9 @@ 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);
|
||||
}
|
|
@ -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;
|
||||
|
@ -288,6 +289,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;
|
||||
#endif
|
||||
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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, ®ion);
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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, ©_info);
|
||||
vkCmdCopyBufferToImage(cmd, upload_heap.heap->value, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_info);
|
||||
mipmap_level++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
1456
rpcs3/Emu/RSX/overlay_controls.h
Normal file
31
rpcs3/Emu/RSX/overlays.cpp
Normal 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
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "rsx_methods.h"
|
||||
#include "Emu/RSX/GCM.h"
|
||||
#include "Common/BufferUtils.h"
|
||||
#include "overlays.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -938,6 +938,9 @@
|
|||
<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>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
|
@ -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>
|
|
@ -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"}},
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
//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();
|
||||
return sdid.GetSelection();
|
||||
selection = sdid.GetSelection();
|
||||
dlg_result = true;
|
||||
});
|
||||
|
||||
while (!dlg_result)
|
||||
{
|
||||
thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
return selection.load();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (auto rsxthr = fxm::get<GSRender>())
|
||||
{
|
||||
if (auto dlg = rsxthr->shell_open_trophy_notification())
|
||||
{
|
||||
return dlg->show(trophy, trophy_icon_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|