mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-13 07:14:49 +00:00
overlays: refactor shader loading dialogs
This commit is contained in:
parent
d94d094a7e
commit
5e7d25ad35
@ -370,6 +370,8 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Overlays/overlays.cpp
|
||||
RSX/Overlays/overlay_shader_compile_notification.cpp
|
||||
RSX/Overlays/overlay_trophy_notification.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog_native.cpp
|
||||
RSX/Capture/rsx_capture.cpp
|
||||
RSX/Capture/rsx_replay.cpp
|
||||
RSX/GL/GLCommonDecompiler.cpp
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "GLGSRender.h"
|
||||
#include "GLCompute.h"
|
||||
#include "GLVertexProgram.h"
|
||||
#include "../Overlays/Shaders/shader_loading_dialog_native.h"
|
||||
#include "../rsx_methods.h"
|
||||
#include "../Common/BufferUtils.h"
|
||||
#include "../rsx_utils.h"
|
||||
@ -911,62 +912,9 @@ void GLGSRender::on_init_thread()
|
||||
}
|
||||
else
|
||||
{
|
||||
struct native_helper : gl::shader_cache::progress_dialog_helper
|
||||
{
|
||||
rsx::thread *owner = nullptr;
|
||||
std::shared_ptr<rsx::overlays::message_dialog> dlg;
|
||||
rsx::shader_loading_dialog_native dlg(this);
|
||||
|
||||
native_helper(GLGSRender *ptr) :
|
||||
owner(ptr) {}
|
||||
|
||||
void create() override
|
||||
{
|
||||
MsgDialogType type = {};
|
||||
type.disable_cancel = true;
|
||||
type.progress_bar_count = 2;
|
||||
|
||||
dlg = g_fxo->get<rsx::overlays::display_manager>()->create<rsx::overlays::message_dialog>(!!g_cfg.video.shader_preloading_dialog.use_custom_background);
|
||||
dlg->progress_bar_set_taskbar_index(-1);
|
||||
dlg->show(false, "Loading precompiled shaders from disk...", type, [](s32 status)
|
||||
{
|
||||
if (status != CELL_OK)
|
||||
Emu.Stop();
|
||||
});
|
||||
}
|
||||
|
||||
void update_msg(u32 index, u32 processed, u32 entry_count) override
|
||||
{
|
||||
const char *text = index == 0 ? "Loading pipeline object %u of %u" : "Compiling pipeline object %u of %u";
|
||||
dlg->progress_bar_set_message(index, fmt::format(text, processed, entry_count));
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void inc_value(u32 index, u32 value) override
|
||||
{
|
||||
dlg->progress_bar_increment(index, static_cast<f32>(value));
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void set_limit(u32 index, u32 limit) override
|
||||
{
|
||||
dlg->progress_bar_set_limit(index, limit);
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void refresh() override
|
||||
{
|
||||
dlg->refresh();
|
||||
}
|
||||
|
||||
void close() override
|
||||
{
|
||||
dlg->return_code = CELL_OK;
|
||||
dlg->close();
|
||||
}
|
||||
}
|
||||
helper(this);
|
||||
|
||||
m_shaders_cache->load(&helper);
|
||||
m_shaders_cache->load(&dlg);
|
||||
}
|
||||
}
|
||||
|
||||
|
91
rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp
Normal file
91
rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include "stdafx.h"
|
||||
#include "shader_loading_dialog.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
void shader_loading_dialog::create(const std::string& msg, const std::string& title)
|
||||
{
|
||||
dlg = Emu.GetCallbacks().get_msg_dialog();
|
||||
if (dlg)
|
||||
{
|
||||
dlg->type.se_normal = true;
|
||||
dlg->type.bg_invisible = true;
|
||||
dlg->type.progress_bar_count = 2;
|
||||
dlg->ProgressBarSetTaskbarIndex(-1); // -1 to combine all progressbars in the taskbar progress
|
||||
dlg->on_close = [](s32 status) { Emu.CallAfter([]() { Emu.Stop(); }); };
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&]()
|
||||
{
|
||||
dlg->Create(msg, title);
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
while (ref_cnt.load() && !Emu.IsStopped())
|
||||
{
|
||||
_mm_pause();
|
||||
}
|
||||
}
|
||||
|
||||
void shader_loading_dialog::update_msg(u32 index, const std::string& msg)
|
||||
{
|
||||
if (!dlg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&, index, msg]()
|
||||
{
|
||||
dlg->ProgressBarSetMsg(index, msg);
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
void shader_loading_dialog::inc_value(u32 index, u32 value)
|
||||
{
|
||||
if (!dlg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&, index, value]()
|
||||
{
|
||||
dlg->ProgressBarInc(index, value);
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
void shader_loading_dialog::set_limit(u32 index, u32 limit)
|
||||
{
|
||||
if (!dlg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&, index, limit]()
|
||||
{
|
||||
dlg->ProgressBarSetLimit(index, limit);
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
void shader_loading_dialog::refresh()
|
||||
{
|
||||
}
|
||||
|
||||
void shader_loading_dialog::close()
|
||||
{
|
||||
while (ref_cnt.load() && !Emu.IsStopped())
|
||||
{
|
||||
_mm_pause();
|
||||
}
|
||||
}
|
||||
}
|
20
rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h
Normal file
20
rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/Modules/cellMsgDialog.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
struct shader_loading_dialog
|
||||
{
|
||||
std::shared_ptr<MsgDialogBase> dlg;
|
||||
atomic_t<int> ref_cnt;
|
||||
|
||||
virtual void create(const std::string& msg, const std::string& title);
|
||||
virtual void update_msg(u32 index, const std::string& msg);
|
||||
virtual void inc_value(u32 index, u32 value);
|
||||
virtual void set_limit(u32 index, u32 limit);
|
||||
virtual void refresh();
|
||||
virtual void close();
|
||||
};
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
#include "stdafx.h"
|
||||
#include "shader_loading_dialog_native.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
shader_loading_dialog_native::shader_loading_dialog_native(GSRender* ptr)
|
||||
: owner(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
void shader_loading_dialog_native::create(const std::string& msg, const std::string&/* title*/)
|
||||
{
|
||||
MsgDialogType type = {};
|
||||
type.disable_cancel = true;
|
||||
type.progress_bar_count = 2;
|
||||
|
||||
dlg = g_fxo->get<rsx::overlays::display_manager>()->create<rsx::overlays::message_dialog>(!!g_cfg.video.shader_preloading_dialog.use_custom_background);
|
||||
dlg->progress_bar_set_taskbar_index(-1);
|
||||
dlg->show(false, msg, type, [](s32 status)
|
||||
{
|
||||
if (status != CELL_OK)
|
||||
Emu.Stop();
|
||||
});
|
||||
}
|
||||
|
||||
void shader_loading_dialog_native::update_msg(u32 index, const std::string& msg)
|
||||
{
|
||||
dlg->progress_bar_set_message(index, msg);
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void shader_loading_dialog_native::inc_value(u32 index, u32 value)
|
||||
{
|
||||
dlg->progress_bar_increment(index, static_cast<f32>(value));
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void shader_loading_dialog_native::set_limit(u32 index, u32 limit)
|
||||
{
|
||||
dlg->progress_bar_set_limit(index, limit);
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void shader_loading_dialog_native::refresh()
|
||||
{
|
||||
dlg->refresh();
|
||||
}
|
||||
|
||||
void shader_loading_dialog_native::close()
|
||||
{
|
||||
dlg->return_code = CELL_OK;
|
||||
dlg->close();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "shader_loading_dialog.h"
|
||||
#include "../../GSRender.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
struct shader_loading_dialog_native : rsx::shader_loading_dialog
|
||||
{
|
||||
rsx::thread* owner = nullptr;
|
||||
std::shared_ptr<rsx::overlays::message_dialog> dlg;
|
||||
|
||||
shader_loading_dialog_native(GSRender* ptr);
|
||||
|
||||
void create(const std::string& msg, const std::string&/* title*/) override;
|
||||
void update_msg(u32 index, const std::string& msg) override;
|
||||
void inc_value(u32 index, u32 value) override;
|
||||
void set_limit(u32 index, u32 limit) override;
|
||||
void refresh() override;
|
||||
void close() override;
|
||||
};
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/System.h"
|
||||
#include "VKGSRender.h"
|
||||
#include "../Overlays/Shaders/shader_loading_dialog_native.h"
|
||||
#include "../rsx_methods.h"
|
||||
#include "../rsx_utils.h"
|
||||
#include "../Common/BufferUtils.h"
|
||||
@ -1879,63 +1880,10 @@ void VKGSRender::on_init_thread()
|
||||
}
|
||||
else
|
||||
{
|
||||
struct native_helper : vk::shader_cache::progress_dialog_helper
|
||||
{
|
||||
rsx::thread *owner = nullptr;
|
||||
std::shared_ptr<rsx::overlays::message_dialog> dlg;
|
||||
|
||||
native_helper(VKGSRender *ptr) :
|
||||
owner(ptr) {}
|
||||
|
||||
void create() override
|
||||
{
|
||||
MsgDialogType type = {};
|
||||
type.disable_cancel = true;
|
||||
type.progress_bar_count = 2;
|
||||
|
||||
dlg = g_fxo->get<rsx::overlays::display_manager>()->create<rsx::overlays::message_dialog>(!!g_cfg.video.shader_preloading_dialog.use_custom_background);
|
||||
dlg->progress_bar_set_taskbar_index(-1);
|
||||
dlg->show(false, "Loading precompiled shaders from disk...", type, [](s32 status)
|
||||
{
|
||||
if (status != CELL_OK)
|
||||
Emu.Stop();
|
||||
});
|
||||
}
|
||||
|
||||
void update_msg(u32 index, u32 processed, u32 entry_count) override
|
||||
{
|
||||
const char *text = index == 0 ? "Loading pipeline object %u of %u" : "Compiling pipeline object %u of %u";
|
||||
dlg->progress_bar_set_message(index, fmt::format(text, processed, entry_count));
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void inc_value(u32 index, u32 value) override
|
||||
{
|
||||
dlg->progress_bar_increment(index, static_cast<f32>(value));
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void set_limit(u32 index, u32 limit) override
|
||||
{
|
||||
dlg->progress_bar_set_limit(index, limit);
|
||||
owner->flip({});
|
||||
}
|
||||
|
||||
void refresh() override
|
||||
{
|
||||
dlg->refresh();
|
||||
}
|
||||
|
||||
void close() override
|
||||
{
|
||||
dlg->return_code = CELL_OK;
|
||||
dlg->close();
|
||||
}
|
||||
}
|
||||
helper(this);
|
||||
rsx::shader_loading_dialog_native dlg(this);
|
||||
|
||||
// TODO: Handle window resize messages during loading on GPUs without OUT_OF_DATE_KHR support
|
||||
m_shaders_cache->load(&helper, *m_device, pipeline_layout);
|
||||
m_shaders_cache->load(&dlg, *m_device, pipeline_layout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,9 @@
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "gcm_enums.h"
|
||||
#include "Common/ProgramStateCache.h"
|
||||
#include "Emu/Cell/Modules/cellMsgDialog.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Common/texture_cache_checker.h"
|
||||
#include "Overlays/Shaders/shader_loading_dialog.h"
|
||||
|
||||
#include "rsx_utils.h"
|
||||
#include <thread>
|
||||
@ -423,104 +423,6 @@ namespace rsx
|
||||
|
||||
public:
|
||||
|
||||
struct progress_dialog_helper
|
||||
{
|
||||
std::shared_ptr<MsgDialogBase> dlg;
|
||||
atomic_t<int> ref_cnt;
|
||||
|
||||
virtual void create()
|
||||
{
|
||||
dlg = Emu.GetCallbacks().get_msg_dialog();
|
||||
if (dlg)
|
||||
{
|
||||
dlg->type.se_normal = true;
|
||||
dlg->type.bg_invisible = true;
|
||||
dlg->type.progress_bar_count = 2;
|
||||
dlg->ProgressBarSetTaskbarIndex(-1); // -1 to combine all progressbars in the taskbar progress
|
||||
dlg->on_close = [](s32 status)
|
||||
{
|
||||
Emu.CallAfter([]()
|
||||
{
|
||||
Emu.Stop();
|
||||
});
|
||||
};
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&]()
|
||||
{
|
||||
dlg->Create("Preloading cached shaders from disk.\nPlease wait...", "Shader Compilation");
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
while (ref_cnt.load() && !Emu.IsStopped())
|
||||
{
|
||||
_mm_pause();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void update_msg(u32 index, u32 processed, u32 entry_count)
|
||||
{
|
||||
if (!dlg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&, index, processed, entry_count]()
|
||||
{
|
||||
const char *text = index == 0 ? "Loading pipeline object %u of %u" : "Compiling pipeline object %u of %u";
|
||||
dlg->ProgressBarSetMsg(index, fmt::format(text, processed, entry_count));
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
virtual void inc_value(u32 index, u32 value)
|
||||
{
|
||||
if (!dlg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&, index, value]()
|
||||
{
|
||||
dlg->ProgressBarInc(index, value);
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
virtual void set_limit(u32 index, u32 limit)
|
||||
{
|
||||
if (!dlg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref_cnt++;
|
||||
|
||||
Emu.CallAfter([&, index, limit]()
|
||||
{
|
||||
dlg->ProgressBarSetLimit(index, limit);
|
||||
ref_cnt--;
|
||||
});
|
||||
}
|
||||
|
||||
virtual void refresh()
|
||||
{}
|
||||
|
||||
virtual void close()
|
||||
{
|
||||
while (ref_cnt.load() && !Emu.IsStopped())
|
||||
{
|
||||
_mm_pause();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
shaders_cache(backend_storage& storage, std::string pipeline_class, std::string version_prefix_str = "v1")
|
||||
: version_prefix(std::move(version_prefix_str))
|
||||
, pipeline_class_name(std::move(pipeline_class))
|
||||
@ -533,7 +435,7 @@ namespace rsx
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void load(progress_dialog_helper* dlg, Args&& ...args)
|
||||
void load(shader_loading_dialog* dlg, Args&& ...args)
|
||||
{
|
||||
if (g_cfg.video.disable_on_disk_shader_cache)
|
||||
{
|
||||
@ -573,18 +475,24 @@ namespace rsx
|
||||
std::vector<std::string> invalid_entries;
|
||||
|
||||
// Progress dialog
|
||||
std::unique_ptr<progress_dialog_helper> fallback_dlg;
|
||||
std::unique_ptr<shader_loading_dialog> fallback_dlg;
|
||||
if (!dlg)
|
||||
{
|
||||
fallback_dlg = std::make_unique<progress_dialog_helper>();
|
||||
fallback_dlg = std::make_unique<shader_loading_dialog>();
|
||||
dlg = fallback_dlg.get();
|
||||
}
|
||||
|
||||
dlg->create();
|
||||
const auto getMessage = [](u32 index, u32 processed, u32 entry_count) -> std::string
|
||||
{
|
||||
const char* text = index == 0 ? "Loading pipeline object %u of %u" : "Compiling pipeline object %u of %u";
|
||||
return fmt::format(text, processed, entry_count);
|
||||
};
|
||||
|
||||
dlg->create("Preloading cached shaders from disk.\nPlease wait...", "Shader Compilation");
|
||||
dlg->set_limit(0, entry_count);
|
||||
dlg->set_limit(1, entry_count);
|
||||
dlg->update_msg(0, 0, entry_count);
|
||||
dlg->update_msg(1, 0, entry_count);
|
||||
dlg->update_msg(0, getMessage(0, 0, entry_count));
|
||||
dlg->update_msg(1, getMessage(0, 0, entry_count));
|
||||
|
||||
// Setup worker threads
|
||||
unsigned nb_threads = std::thread::hardware_concurrency();
|
||||
@ -620,7 +528,7 @@ namespace rsx
|
||||
processed_since_last_update++;
|
||||
if ((std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update) > 100ms) || (i == entry_count - 1))
|
||||
{
|
||||
dlg->update_msg(0, i + 1, entry_count);
|
||||
dlg->update_msg(0, getMessage(0, i + 1, entry_count));
|
||||
dlg->inc_value(0, processed_since_last_update);
|
||||
last_update = now;
|
||||
processed_since_last_update = 0;
|
||||
@ -663,7 +571,7 @@ namespace rsx
|
||||
|
||||
if (processed_since_last_update > 0)
|
||||
{
|
||||
dlg->update_msg(1, current_progress, entry_count);
|
||||
dlg->update_msg(1, getMessage(0, current_progress, entry_count));
|
||||
dlg->inc_value(1, processed_since_last_update);
|
||||
}
|
||||
}
|
||||
@ -685,7 +593,7 @@ namespace rsx
|
||||
processed_since_last_update++;
|
||||
if ((std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update) > 100ms) || (pos == entry_count - 1))
|
||||
{
|
||||
dlg->update_msg(1, pos + 1, entry_count);
|
||||
dlg->update_msg(1, getMessage(0, pos + 1, entry_count));
|
||||
dlg->inc_value(1, processed_since_last_update);
|
||||
last_update = now;
|
||||
processed_since_last_update = 0;
|
||||
|
@ -70,6 +70,8 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Emu\Io\KeyboardHandler.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.cpp" />
|
||||
<ClCompile Include="util\atomic.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@ -395,6 +397,8 @@
|
||||
<ClInclude Include="Emu\Io\Keyboard.h" />
|
||||
<ClInclude Include="Emu\RSX\Common\texture_cache_helpers.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\overlay_perf_metrics.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.h" />
|
||||
<ClInclude Include="util\atomic.hpp" />
|
||||
<ClInclude Include="..\Utilities\BEType.h" />
|
||||
<ClInclude Include="..\Utilities\bin_patch.h" />
|
||||
|
@ -69,6 +69,9 @@
|
||||
<Filter Include="Emu\GPU\RSX\Overlays">
|
||||
<UniqueIdentifier>{7536c5ad-d58f-40a7-96db-74d959fa4c02}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Emu\GPU\RSX\Overlays\Shaders">
|
||||
<UniqueIdentifier>{f368580d-07bc-4c8b-b488-a4198f2dd17d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Crypto\aes.cpp">
|
||||
@ -836,6 +839,12 @@
|
||||
<ClCompile Include="Crypto\aesni.cpp">
|
||||
<Filter>Crypto</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.cpp">
|
||||
<Filter>Emu\GPU\RSX\Overlays\Shaders</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp">
|
||||
<Filter>Emu\GPU\RSX\Overlays\Shaders</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -1585,5 +1594,11 @@
|
||||
<ClInclude Include="Emu\Cell\Modules\cellStorage.h">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.h">
|
||||
<Filter>Emu\GPU\RSX\Overlays\Shaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.h">
|
||||
<Filter>Emu\GPU\RSX\Overlays\Shaders</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user