This commit is contained in:
iwubcode 2025-03-15 13:20:59 -04:00 committed by GitHub
commit 5dc1b8f187
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
235 changed files with 21834 additions and 3312 deletions

3
.gitmodules vendored
View file

@ -87,3 +87,6 @@
[submodule "Externals/SFML/SFML"]
path = Externals/SFML/SFML
url = https://github.com/SFML/SFML.git
[submodule "Externals/watcher/watcher"]
path = Externals/watcher/watcher
url = https://github.com/e-dant/watcher

View file

@ -768,6 +768,8 @@ if (USE_RETRO_ACHIEVEMENTS)
add_subdirectory(Externals/rcheevos)
endif()
add_subdirectory(Externals/watcher)
########################################
# Pre-build events: Define configuration variables and write SCM info header
#

View file

@ -0,0 +1,8 @@
void fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput frag_output)
{
dolphin_emulated_fragment(frag_input, frag_output);
vec3 tint_color = vec3(0.0, 0.0, 1.0);
vec3 input_color = frag_output.main.rgb / 255.0;
vec3 output_color = (0.5 * input_color) + (0.5 * tint_color.rgb);
frag_output.main.rgb = ivec3(output_color * 255);
}

View file

@ -0,0 +1,6 @@
{
"pixel_properties":[],
"pixel_output_targets":[],
"pixel_samplers":[],
"vertex_properties":[]
}

View file

@ -0,0 +1,4 @@
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
{
dolphin_emulated_vertex(vertex_input, vertex_output);
}

View file

@ -0,0 +1,8 @@
{
"next_material_asset":"",
"pixel_properties":[],
"pixel_textures":[],
"render_targets":[],
"shader_asset": "highlight_shader",
"vertex_properties":[]
}

View file

@ -0,0 +1,12 @@
void fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput frag_output)
{
if (frag_input.normal.xyz == vec3(0, 0, 0))
{
dolphin_emulated_fragment(frag_input, frag_output);
}
else
{
vec4 output_color = dolphin_calculate_lighting_chn0(frag_input.color_0, vec4(frag_input.position, 1), frag_input.normal);
frag_output.main = ivec4(output_color * 255.0);
}
}

View file

@ -0,0 +1,8 @@
{
"next_material_asset":"",
"pixel_properties":[],
"pixel_textures":[],
"render_targets":[],
"shader_asset": "simple_light_visualization_shader",
"vertex_properties":[]
}

View file

@ -0,0 +1,6 @@
{
"pixel_properties":[],
"pixel_output_targets":[],
"pixel_samplers":[],
"vertex_properties":[]
}

View file

@ -0,0 +1,4 @@
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
{
dolphin_emulated_vertex(vertex_input, vertex_output);
}

View file

@ -0,0 +1,12 @@
void fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput frag_output)
{
if (frag_input.normal.xyz == vec3(0, 0, 0))
{
dolphin_emulated_fragment(frag_input, frag_output);
}
else
{
vec4 output_color = vec4(frag_input.normal.xyz * 0.5 + 0.5, 1);
frag_output.main = ivec4(output_color * 255.0);
}
}

View file

@ -0,0 +1,8 @@
{
"next_material_asset":"",
"pixel_properties":[],
"pixel_textures":[],
"render_targets":[],
"shader_asset": "normal_visualization_shader",
"vertex_properties":[]
}

View file

@ -0,0 +1,6 @@
{
"pixel_properties":[],
"pixel_output_targets":[],
"pixel_samplers":[],
"vertex_properties":[]
}

View file

@ -0,0 +1,4 @@
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
{
dolphin_emulated_vertex(vertex_input, vertex_output);
}

View file

@ -0,0 +1,8 @@
{
"shader_asset": "",
"pixel_properties": [],
"vertex_properties": [],
"next_material_asset": "",
"pixel_textures": [],
"render_targets": []
}

View file

@ -0,0 +1,6 @@
{
"pixel_properties": [],
"pixel_output_targets": [],
"pixel_samplers": [],
"vertex_properties": []
}

View file

@ -0,0 +1,4 @@
void fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput frag_output)
{
dolphin_emulated_fragment(frag_input, frag_output);
}

View file

@ -0,0 +1,4 @@
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
{
dolphin_emulated_vertex(vertex_input, vertex_output);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

View file

@ -1,15 +1,22 @@
{
"meta":
{
"title": "Bloom Removal",
"author": "Dolphin Team",
"description": "Skips drawing bloom effects. May be preferable when using a bloom solution from Dolphin's post processing shaders or a third party tool."
},
"features":
[
{
"group": "Bloom",
"action": "skip"
}
]
}
"actions": [
{
"data": {},
"factory_name": "skip"
}
],
"assets": [
],
"meta": {
"author": "Dolphin Team",
"description": "Skips drawing bloom effects. May be preferable when using a bloom solution from Dolphin's post processing shaders or a third party tool.",
"mod_version": "1.0.0",
"schema_version": 1,
"title": "Bloom Removal"
},
"tag_to_actions": {
"Bloom": [
0
]
}
}

View file

@ -1,15 +1,22 @@
{
"meta":
{
"title": "DOF Removal",
"author": "Dolphin Team",
"description": "Skips drawing DOF effects. May be preferable when using a DOF solution from Dolphin's post processing shaders or a third party tool."
},
"features":
[
{
"group": "DOF",
"action": "skip"
}
]
"actions": [
{
"data": {},
"factory_name": "skip"
}
],
"assets": [
],
"meta": {
"author": "Dolphin Team",
"description": "Skips drawing DOF effects. May be preferable when using a DOF solution from Dolphin's post processing shaders or a third party tool.",
"mod_version": "1.0.0",
"schema_version": 1,
"title": "DOF Removal"
},
"tag_to_actions": {
"Depth of Field": [
0
]
}
}

View file

@ -1,14 +1,22 @@
{
"meta":
{
"title": "Remove HUD",
"author": "Dolphin Team",
"description": "Skips drawing elements designated as the HUD. Can be used for taking screenshots or increasing immersion."
},
"features": [
{
"group": "HUD",
"action": "skip"
}
]
}
"actions": [
{
"data": {},
"factory_name": "skip"
}
],
"assets": [
],
"meta": {
"author": "Dolphin Team",
"description": "Skips drawing elements designated as the HUD. Can be used for taking screenshots or increasing immersion.",
"mod_version": "1.0.0",
"schema_version": 1,
"title": "Remove HUD"
},
"tag_to_actions": {
"User Interface": [
0
]
}
}

View file

@ -0,0 +1,66 @@
float4 SampleTexmap(uint texmap, float3 coords)
{
for (uint i = 0; i < 8; i++)
{
if (texmap == i)
{
return texture(samp[i], coords);
}
}
return float4(0, 0, 0, 1);
}
float2 GetTextureSize(uint texmap)
{
for (uint i = 0; i < 8; i++)
{
if (texmap == i)
{
return float2(textureSize(samp[i], 0));
}
}
return float2(0, 0);
}
vec4 custom_main( in CustomShaderData data )
{
if (data.texcoord_count == 0)
{
return data.final_color;
}
if (data.tev_stage_count == 0)
{
return data.final_color;
}
uint efb = data.tev_stages[0].texmap;
float3 coords = data.texcoord[0];
float4 out_color = SampleTexmap(efb, coords);
float2 size = GetTextureSize(efb);
// If options are added to the UI, include custom radius and intensity, radius should be around IR - 1.
// Small values decrease bloom area, but can lead to broken bloom if too small.
float intensity = 1.0;
float radius = 3;
float dx = 1.0/size.x;
float dy = 1.0/size.y;
float x;
float y;
float count = 1.0;
float4 color = float4(0.0, 0.0, 0.0, 0.0);
for (x = -radius; x <= radius; x++)
{
for (y = -radius; y <= radius; y++)
{
count += 1.0;
float3 off_coords = float3(coords.x + x*dx, coords.y + y*dy, coords.z);
color += SampleTexmap(efb, off_coords);
}
}
out_color = color / count * intensity;
return out_color;
}

View file

@ -0,0 +1,4 @@
{
"shader_asset": "bloom_shader",
"values": []
}

View file

@ -0,0 +1,3 @@
{
"properties": []
}

View file

@ -1,20 +1,42 @@
{
"meta":
{
"title": "Native Resolution Bloom",
"author": "Dolphin Team",
"description": "Scales bloom effects to draw at their native resolution, regardless of internal resolution. Results in bloom looking much more natural at higher resolutions but may cause shimmering."
},
"features":
[
{
"group": "Bloom",
"action": "scale",
"action_data": {
"X": 1.0,
"Y": 1.0,
"Z": 1.0
}
}
]
}
"actions": [
{
"data": {
"active": true,
"passes": [
{
"pixel_material_asset": "bloom_material"
}
]
},
"factory_name": "custom_pipeline"
}
],
"assets": [
{
"data": {
"metadata": "bloom.shader",
"shader": "bloom.glsl"
},
"id": "bloom_shader"
},
{
"data": {
"metadata": "bloom.material"
},
"id": "bloom_material"
}
],
"meta": {
"author": "Dolphin Team",
"description": "Scales bloom effects to draw at their native resolution, regardless of internal resolution. Results in bloom looking much more natural at higher resolutions.",
"mod_version": "1.0.0",
"schema_version": 1,
"title": "Native Resolution Bloom"
},
"tag_to_actions": {
"Bloom": [
0
]
}
}

View file

@ -0,0 +1,66 @@
float4 SampleTexmap(uint texmap, float3 coords)
{
for (uint i = 0; i < 8; i++)
{
if (texmap == i)
{
return texture(samp[i], coords);
}
}
return float4(0, 0, 0, 1);
}
float2 GetTextureSize(uint texmap)
{
for (uint i = 0; i < 8; i++)
{
if (texmap == i)
{
return float2(textureSize(samp[i], 0));
}
}
return float2(0, 0);
}
vec4 custom_main( in CustomShaderData data )
{
if (data.texcoord_count == 0)
{
return data.final_color;
}
if (data.tev_stage_count == 0)
{
return data.final_color;
}
uint efb = data.tev_stages[0].texmap;
float3 coords = data.texcoord[0];
float4 out_color = SampleTexmap(efb, coords);
float2 size = GetTextureSize(efb);
// If options are added to the UI, include custom radius and intensity, radius should be around IR - 1.
// Small values decrease bloom area, but can lead to broken bloom if too small.
float intensity = 1.0;
float radius = 3;
float dx = 1.0/size.x;
float dy = 1.0/size.y;
float x;
float y;
float count = 1.0;
float4 color = float4(0.0, 0.0, 0.0, 0.0);
for (x = -radius; x <= radius; x++)
{
for (y = -radius; y <= radius; y++)
{
count += 1.0;
float3 off_coords = float3(coords.x + x*dx, coords.y + y*dy, coords.z);
color += SampleTexmap(efb, off_coords);
}
}
out_color = color / count * intensity;
return out_color;
}

View file

@ -0,0 +1,4 @@
{
"shader_asset": "dof_shader",
"values": []
}

View file

@ -0,0 +1,3 @@
{
"properties": []
}

View file

@ -1,20 +1,42 @@
{
"meta":
{
"title": "Native Resolution DOF",
"author": "Dolphin Team",
"description": "Scales DOF effects to draw at their native resolution, regardless of internal resolution. Results in DOF looking much more natural at higher resolutions but may cause shimmering."
},
"features":
[
{
"group": "DOF",
"action": "scale",
"action_data": {
"X": 1.0,
"Y": 1.0,
"Z": 1.0
}
}
]
"actions": [
{
"data": {
"active": true,
"passes": [
{
"pixel_material_asset": "dof_material"
}
]
},
"factory_name": "custom_pipeline"
}
],
"assets": [
{
"data": {
"metadata": "dof.shader",
"shader": "dof.glsl"
},
"id": "dof_shader"
},
{
"data": {
"metadata": "dof.material"
},
"id": "dof_material"
}
],
"meta": {
"author": "Dolphin Team",
"description": "Scales depth of field (dof) effects to draw at their native resolution, regardless of internal resolution. Results for dof looking much more natural at higher resolutions.",
"mod_version": "1.0.0",
"schema_version": 1,
"title": "Native Resolution DOF"
},
"tag_to_actions": {
"Depth of Field": [
0
]
}
}

View file

@ -1,19 +1,29 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n33_160x112_6"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Arc Rise Fantasia Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {
"0": []
},
"targets": [
{
"id": "16030373046293997871",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,19 +1,27 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n2_320x224_4"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Donkey Kong Country Returns Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "3405476862620419263",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,27 +1,27 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n3_80x56_6"
},
{
"type": "efb",
"texture_filename": "efb1_n2_160x112_6"
},
{
"type": "efb",
"texture_filename": "efb1_n6_320x224_6"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Monster Hunter Tri Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "13233451943079225832",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,19 +1,27 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000019_128x128_4"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Nights Journey of Dreams Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "13920250048690583912",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,19 +1,27 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n51_320x240_6"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Okami Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "6230608514450344714",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,36 +1,27 @@
{
"meta":
{
"title": "Bloom and DOF Texture Definitions",
"author": "linckandrea"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n09_20x15_1"
},
{
"type": "efb",
"texture_filename": "efb1_n21_20x15_1"
}
]
},
{
"name": "DOF",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n10_320x240_4"
},
{
"type": "efb",
"texture_filename": "efb1_n11_320x240_1"
}
]
}
]
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Pandoras Tower Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "10512703869395082884",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,31 +1,47 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000022_40x28_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000021_80x56_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000020_160x112_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000025_320x224_6"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "The Conduit Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {
"0": [],
"1": [],
"2": []
},
"targets": [
{
"id": "1759966615601106229",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "2423050791446092715",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "4157223477088026000",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,23 +1,27 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n55_80x57_6"
},
{
"type": "efb",
"texture_filename": "efb1_n54_160x114_6"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Twilight Princess Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "10063434684210657575",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,27 +1,43 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n9_80x58_6"
},
{
"type": "efb",
"texture_filename": "efb1_n21_80x57_6"
},
{
"type": "efb",
"texture_filename": "efb1_n2_320x228_6"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Wii Play Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "5238528733911143545",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "10820371786676733846",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "18418729168554791402",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -1,31 +1,67 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n15_20x16_4"
},
{
"type": "efb",
"texture_filename": "efb1_n9_40x30_4"
},
{
"type": "efb",
"texture_filename": "efb1_n7_80x58_4"
},
{
"type": "efb",
"texture_filename": "efb1_n1_320x228_4"
}
]
}
]
}
"actions": [],
"assets": [],
"default_hash_policy": {
"attributes": ""
},
"meta": {
"author": "iwubcode",
"description": "",
"mod_version": "",
"schema_version": 1,
"title": "Xenoblade Definitions"
},
"tag_to_actions": {},
"tags": [],
"target_to_actions": {},
"targets": [
{
"id": "1873769556004204196",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "2784022002606692122",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "5940037079112913957",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "8606219869014623335",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "11236664535429889509",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
},
{
"id": "15824374617827814652",
"name": "",
"tags": [
"Bloom"
],
"type": "int"
}
]
}

View file

@ -10,6 +10,7 @@ set(SRCS
imgui_draw.cpp
imgui_tables.cpp
imgui_widgets.cpp
misc/cpp/imgui_stdlib.cpp
)
add_library(imgui STATIC ${SRCS})

View file

@ -33,7 +33,7 @@
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.

4
Externals/watcher/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,4 @@
add_library(watcher INTERFACE IMPORTED GLOBAL)
set_target_properties(watcher PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/watcher/include
)

1
Externals/watcher/watcher vendored Submodule

@ -0,0 +1 @@
Subproject commit 0d6b9b409ccaed6313437ea3dc8b2fc078f3d25b

View file

@ -5,13 +5,14 @@
#include <jni.h>
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
static GraphicsModConfig* GetPointer(JNIEnv* env, jobject obj)
static GraphicsModSystem::Config::GraphicsModGroup::GraphicsModWithMetadata* GetPointer(JNIEnv* env,
jobject obj)
{
return reinterpret_cast<GraphicsModConfig*>(
return reinterpret_cast<GraphicsModSystem::Config::GraphicsModGroup::GraphicsModWithMetadata*>(
env->GetLongField(obj, IDCache::GetGraphicsModPointer()));
}
@ -20,20 +21,20 @@ extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_getName(JNIEnv* env, jobject obj)
{
return ToJString(env, GetPointer(env, obj)->m_title);
return ToJString(env, GetPointer(env, obj)->m_mod.m_title);
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_getCreator(JNIEnv* env,
jobject obj)
{
return ToJString(env, GetPointer(env, obj)->m_author);
return ToJString(env, GetPointer(env, obj)->m_mod.m_author);
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_getNotes(JNIEnv* env, jobject obj)
{
return ToJString(env, GetPointer(env, obj)->m_description);
return ToJString(env, GetPointer(env, obj)->m_mod.m_description);
}
JNIEXPORT jboolean JNICALL

View file

@ -3,21 +3,21 @@
#include <jni.h>
#include <set>
#include <vector>
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
static GraphicsModGroupConfig* GetPointer(JNIEnv* env, jobject obj)
static GraphicsModSystem::Config::GraphicsModGroup* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<GraphicsModGroupConfig*>(
return reinterpret_cast<GraphicsModSystem::Config::GraphicsModGroup*>(
env->GetLongField(obj, IDCache::GetGraphicsModGroupPointer()));
}
jobject GraphicsModToJava(JNIEnv* env, GraphicsModConfig* mod, jobject jGraphicsModGroup)
jobject GraphicsModToJava(JNIEnv* env,
GraphicsModSystem::Config::GraphicsModGroup::GraphicsModWithMetadata* mod,
jobject jGraphicsModGroup)
{
return env->NewObject(IDCache::GetGraphicsModClass(), IDCache::GetGraphicsModConstructor(),
reinterpret_cast<jlong>(mod), jGraphicsModGroup);
@ -36,34 +36,23 @@ JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_getMods(JNIEnv* env,
jobject obj)
{
GraphicsModGroupConfig* mod_group = GetPointer(env, obj);
GraphicsModSystem::Config::GraphicsModGroup* mod_group = GetPointer(env, obj);
std::vector<GraphicsModSystem::Config::GraphicsModGroup::GraphicsModWithMetadata*> mods;
std::set<std::string> groups;
for (const GraphicsModConfig& mod : mod_group->GetMods())
for (auto& mod : mod_group->GetMods())
{
for (const GraphicsTargetGroupConfig& group : mod.m_groups)
groups.insert(group.m_name);
}
std::vector<GraphicsModConfig*> mods;
for (GraphicsModConfig& mod : mod_group->GetMods())
{
// If no group matches the mod's features, or if the mod has no features, skip it
if (std::ranges::none_of(mod.m_features, [&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
{
if (mod.m_mod.m_actions.empty())
continue;
}
mods.push_back(&mod);
}
return VectorToJObjectArray(
env, mods, IDCache::GetGraphicsModClass(),
[obj](JNIEnv* env, GraphicsModConfig* mod) { return GraphicsModToJava(env, mod, obj); });
[obj](JNIEnv* env,
GraphicsModSystem::Config::GraphicsModGroup::GraphicsModWithMetadata* mod) {
return GraphicsModToJava(env, mod, obj);
});
}
JNIEXPORT void JNICALL
@ -76,7 +65,7 @@ JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_load(JNIEnv* env, jclass,
jstring jGameId)
{
auto* mod_group = new GraphicsModGroupConfig(GetJString(env, jGameId));
auto* mod_group = new GraphicsModSystem::Config::GraphicsModGroup(GetJString(env, jGameId));
mod_group->Load();

View file

@ -89,6 +89,7 @@ add_library(common
IOFile.h
JitRegister.cpp
JitRegister.h
JsonUtil.cpp
JsonUtil.h
JsonUtil.cpp
Lazy.h

View file

@ -61,6 +61,7 @@
#define RIIVOLUTION_DIR "Riivolution"
#define DUMP_DIR "Dump"
#define DUMP_TEXTURES_DIR "Textures"
#define DUMP_MESHES_DIR "Meshes"
#define DUMP_FRAMES_DIR "Frames"
#define DUMP_OBJECTS_DIR "Objects"
#define DUMP_AUDIO_DIR "Audio"
@ -89,6 +90,7 @@
#define GRAPHICSMOD_DIR "GraphicMods"
#define WIISDSYNC_DIR "WiiSDSync"
#define ASSEMBLY_DIR "SavedAssembly"
#define GRAPHICSMODEDITOR_DIR "GraphicsModEditor"
// This one is only used to remove it if it was present
#define SHADERCACHE_LEGACY_DIR "ShaderCache"

View file

@ -869,6 +869,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_DUMPOBJECTS_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_OBJECTS_DIR DIR_SEP;
s_user_paths[D_DUMPAUDIO_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
s_user_paths[D_DUMPTEXTURES_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
s_user_paths[D_DUMPMESHES_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_MESHES_DIR DIR_SEP;
s_user_paths[D_DUMPDSP_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP;
s_user_paths[D_DUMPSSL_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_SSL_DIR DIR_SEP;
s_user_paths[D_DUMPDEBUG_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DEBUG_DIR DIR_SEP;
@ -954,6 +955,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_DUMPOBJECTS_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_OBJECTS_DIR DIR_SEP;
s_user_paths[D_DUMPAUDIO_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
s_user_paths[D_DUMPTEXTURES_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
s_user_paths[D_DUMPMESHES_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_MESHES_DIR DIR_SEP;
s_user_paths[D_DUMPDSP_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP;
s_user_paths[D_DUMPSSL_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_SSL_DIR DIR_SEP;
s_user_paths[D_DUMPDEBUG_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DEBUG_DIR DIR_SEP;

View file

@ -51,6 +51,7 @@ enum
D_DUMPOBJECTS_IDX,
D_DUMPAUDIO_IDX,
D_DUMPTEXTURES_IDX,
D_DUMPMESHES_IDX,
D_DUMPDSP_IDX,
D_DUMPSSL_IDX,
D_DUMPDEBUG_IDX,

View file

@ -36,6 +36,9 @@
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/GeometryShaderManager.h"
#include "VideoCommon/GraphicsModEditor/EditorMain.h"
#include "VideoCommon/GraphicsModSystem/Runtime/CustomResourceManager.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
#include "VideoCommon/PixelEngine.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/VertexShaderManager.h"
@ -95,9 +98,12 @@ struct System::Impl
Interpreter m_interpreter;
JitInterface m_jit_interface;
VideoCommon::CustomAssetLoader m_custom_asset_loader;
VideoCommon::CustomResourceManager m_custom_resource_manager;
FifoPlayer m_fifo_player;
FifoRecorder m_fifo_recorder;
Movie::MovieManager m_movie;
GraphicsModEditor::EditorMain m_graphics_mod_editor;
GraphicsModSystem::Runtime::GraphicsModManager m_graphics_mod_manager;
};
System::System() : m_impl{std::make_unique<Impl>(*this)}
@ -332,4 +338,18 @@ VideoCommon::CustomAssetLoader& System::GetCustomAssetLoader() const
{
return m_impl->m_custom_asset_loader;
}
VideoCommon::CustomResourceManager& System::GetCustomResourceManager() const
{
return m_impl->m_custom_resource_manager;
}
GraphicsModEditor::EditorMain& System::GetGraphicsModEditor() const
{
return m_impl->m_graphics_mod_editor;
}
GraphicsModSystem::Runtime::GraphicsModManager& System::GetGraphicsModManager() const
{
return m_impl->m_graphics_mod_manager;
}
} // namespace Core

View file

@ -53,6 +53,14 @@ namespace GPFifo
{
class GPFifoManager;
}
namespace GraphicsModEditor
{
class EditorMain;
}
namespace GraphicsModSystem::Runtime
{
class GraphicsModManager;
}
namespace IOS::HLE
{
class EmulationKernel;
@ -108,7 +116,8 @@ class SystemTimersManager;
namespace VideoCommon
{
class CustomAssetLoader;
}
class CustomResourceManager;
} // namespace VideoCommon
namespace VideoInterface
{
class VideoInterfaceManager;
@ -194,6 +203,9 @@ public:
XFStateManager& GetXFStateManager() const;
VideoInterface::VideoInterfaceManager& GetVideoInterface() const;
VideoCommon::CustomAssetLoader& GetCustomAssetLoader() const;
VideoCommon::CustomResourceManager& GetCustomResourceManager() const;
GraphicsModEditor::EditorMain& GetGraphicsModEditor() const;
GraphicsModSystem::Runtime::GraphicsModManager& GetGraphicsModManager() const;
private:
System();

View file

@ -658,12 +658,18 @@
<ClInclude Include="VideoCommon\Assets\CustomAsset.h" />
<ClInclude Include="VideoCommon\Assets\CustomAssetLibrary.h" />
<ClInclude Include="VideoCommon\Assets\CustomAssetLoader.h" />
<ClInclude Include="VideoCommon\Assets\CustomAssetLoader2.h" />
<ClInclude Include="VideoCommon\Assets\CustomTextureData.h" />
<ClInclude Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.h" />
<ClInclude Include="VideoCommon\Assets\MaterialAsset.h" />
<ClInclude Include="VideoCommon\Assets\MaterialAssetUtils.h" />
<ClInclude Include="VideoCommon\Assets\MeshAsset.h" />
<ClInclude Include="VideoCommon\Assets\RenderTargetAsset.h" />
<ClInclude Include="VideoCommon\Assets\ShaderAsset.h" />
<ClInclude Include="VideoCommon\Assets\TextureAsset.h" />
<ClInclude Include="VideoCommon\Assets\TextureSamplerValue.h" />
<ClInclude Include="VideoCommon\Assets\Types.h" />
<ClInclude Include="VideoCommon\Assets\WatchableFilesystemAssetLibrary.h" />
<ClInclude Include="VideoCommon\AsyncRequests.h" />
<ClInclude Include="VideoCommon\AsyncShaderCompiler.h" />
<ClInclude Include="VideoCommon\BoundingBox.h" />
@ -687,26 +693,65 @@
<ClInclude Include="VideoCommon\FreeLookCamera.h" />
<ClInclude Include="VideoCommon\GeometryShaderGen.h" />
<ClInclude Include="VideoCommon\GeometryShaderManager.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\AssetDisplay.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\CameraChoiceControl.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\MaterialControl.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\MaterialGenerateWindow.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\MeshControl.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\MeshExtractWindow.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\MeshImportWindow.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\MiscControls.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\RenderTargetControl.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\ShaderControl.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\TagSelectionWindow.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Controls\TextureControl.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\EditorAssetSource.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\EditorBackend.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\EditorEvents.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\EditorFilter.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\EditorMain.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\EditorState.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\EditorTypes.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Panels\ActiveTargetsPanel.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Panels\AssetBrowserPanel.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\Panels\PropertiesPanel.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\MaterialGeneration.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\RenderTargetGeneration.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\SceneDumper.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\SceneUtils.h" />
<ClInclude Include="VideoCommon\GraphicsModEditor\ShaderGeneration.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModAsset.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModHashPolicy.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModTag.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Constants.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\CustomMeshAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\CustomPipelineAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ModifyLight.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\RelativeCameraAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CustomPipeline.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CustomShaderCache.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\TransformAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CameraManager.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CustomResourceManager.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CustomShaderCache2.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CustomTextureCache.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CustomTextureCache2.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionData.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionFactory.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModBackend.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModHash.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModGroup.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModManager.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModRuntimeBackend.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Types.h" />
<ClInclude Include="VideoCommon\GXPipelineTypes.h" />
<ClInclude Include="VideoCommon\HiresTextures.h" />
<ClInclude Include="VideoCommon\ImageWrite.h" />
@ -1304,12 +1349,17 @@
<ClCompile Include="VideoCommon\Assets\CustomAsset.cpp" />
<ClCompile Include="VideoCommon\Assets\CustomAssetLibrary.cpp" />
<ClCompile Include="VideoCommon\Assets\CustomAssetLoader.cpp" />
<ClCompile Include="VideoCommon\Assets\CustomAssetLoader2.cpp" />
<ClCompile Include="VideoCommon\Assets\CustomTextureData.cpp" />
<ClCompile Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.cpp" />
<ClCompile Include="VideoCommon\Assets\MaterialAsset.cpp" />
<ClCompile Include="VideoCommon\Assets\MaterialAssetUtils.cpp" />
<ClCompile Include="VideoCommon\Assets\MeshAsset.cpp" />
<ClCompile Include="VideoCommon\Assets\RenderTargetAsset.cpp" />
<ClCompile Include="VideoCommon\Assets\ShaderAsset.cpp" />
<ClCompile Include="VideoCommon\Assets\TextureAsset.cpp" />
<ClCompile Include="VideoCommon\Assets\TextureSamplerValue.cpp" />
<ClCompile Include="VideoCommon\Assets\WatchableFilesystemAssetLibrary.cpp" />
<ClCompile Include="VideoCommon\AsyncRequests.cpp" />
<ClCompile Include="VideoCommon\AsyncShaderCompiler.cpp" />
<ClCompile Include="VideoCommon\BoundingBox.cpp" />
@ -1329,22 +1379,58 @@
<ClCompile Include="VideoCommon\FreeLookCamera.cpp" />
<ClCompile Include="VideoCommon\GeometryShaderGen.cpp" />
<ClCompile Include="VideoCommon\GeometryShaderManager.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\AssetDisplay.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\CameraChoiceControl.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\MaterialControl.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\MaterialGenerateWindow.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\MeshControl.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\MeshExtractWindow.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\MeshImportWindow.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\MiscControls.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\RenderTargetControl.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\ShaderControl.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\TagSelectionWindow.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Controls\TextureControl.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\EditorAssetSource.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\EditorBackend.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\EditorFilter.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\EditorMain.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\EditorState.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Panels\ActiveTargetsPanel.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Panels\AssetBrowserPanel.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\Panels\PropertiesPanel.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\MaterialGeneration.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\RenderTargetGeneration.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\SceneDumper.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\SceneUtils.cpp" />
<ClCompile Include="VideoCommon\GraphicsModEditor\ShaderGeneration.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModAsset.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModHashPolicy.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModTag.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\CustomMeshAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\CustomPipelineAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ModifyLight.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\RelativeCameraAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CustomPipeline.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CustomShaderCache.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\TransformAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CameraManager.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CustomResourceManager.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CustomShaderCache2.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CustomTextureCache.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CustomTextureCache2.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionFactory.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModBackend.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModHash.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModManager.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModRuntimeBackend.cpp" />
<ClCompile Include="VideoCommon\HiresTextures.cpp" />
<ClCompile Include="VideoCommon\IndexGenerator.cpp" />
<ClCompile Include="VideoCommon\LightingShaderGen.cpp" />

View file

@ -23,7 +23,6 @@
#include "DolphinQt/QtUtils/ClearLayoutRecursively.h"
#include "DolphinQt/Settings.h"
#include "UICommon/GameFile.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/VideoConfig.h"
GraphicsModListWidget::GraphicsModListWidget(const UICommon::GameFile& game)
@ -38,7 +37,7 @@ GraphicsModListWidget::GraphicsModListWidget(const UICommon::GameFile& game)
ConnectWidgets();
RefreshModList();
OnModChanged(std::nullopt);
OnModChanged(0);
}
GraphicsModListWidget::~GraphicsModListWidget()
@ -124,30 +123,17 @@ void GraphicsModListWidget::RefreshModList()
m_mod_list->setCurrentItem(nullptr);
m_mod_list->clear();
m_mod_group = GraphicsModGroupConfig(m_game_id);
m_mod_group = GraphicsModSystem::Config::GraphicsModGroup(m_game_id);
m_mod_group.Load();
std::set<std::string> groups;
for (const GraphicsModConfig& mod : m_mod_group.GetMods())
for (const auto& mod : m_mod_group.GetMods())
{
for (const GraphicsTargetGroupConfig& group : mod.m_groups)
groups.insert(group.m_name);
}
for (const GraphicsModConfig& mod : m_mod_group.GetMods())
{
// If no group matches the mod's features, or if the mod has no features, skip it
if (std::ranges::none_of(mod.m_features, [&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
{
if (mod.m_mod.m_actions.empty())
continue;
}
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(mod.m_title));
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(mod.m_mod.m_title));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setData(Qt::UserRole, QString::fromStdString(mod.GetAbsolutePath()));
item->setData(Qt::UserRole, static_cast<qulonglong>(mod.m_id));
item->setCheckState(mod.m_enabled ? Qt::Checked : Qt::Unchecked);
m_mod_list->addItem(item);
@ -160,14 +146,13 @@ void GraphicsModListWidget::ModSelectionChanged()
return;
if (m_mod_list->count() == 0)
return;
const auto absolute_path = m_mod_list->currentItem()->data(Qt::UserRole).toString().toStdString();
OnModChanged(absolute_path);
OnModChanged(m_mod_list->currentItem()->data(Qt::UserRole).toULongLong());
}
void GraphicsModListWidget::ModItemChanged(QListWidgetItem* item)
{
const auto absolute_path = item->data(Qt::UserRole).toString();
GraphicsModConfig* mod = m_mod_group.GetMod(absolute_path.toStdString());
const auto id = item->data(Qt::UserRole).toULongLong();
auto mod = m_mod_group.GetMod(id);
if (!mod)
return;
@ -185,39 +170,39 @@ void GraphicsModListWidget::ModItemChanged(QListWidgetItem* item)
m_needs_save = true;
}
void GraphicsModListWidget::OnModChanged(const std::optional<std::string>& absolute_path)
void GraphicsModListWidget::OnModChanged(u64 id)
{
ClearLayoutRecursively(m_mod_meta_layout);
adjustSize();
if (!absolute_path)
if (id == 0)
{
m_selected_mod_name->setText(tr("No graphics mod selected"));
m_selected_mod_name->setAlignment(Qt::AlignCenter);
return;
}
const GraphicsModConfig* mod = m_mod_group.GetMod(*absolute_path);
const auto mod = m_mod_group.GetMod(id);
if (!mod)
return;
m_selected_mod_name->setText(QString::fromStdString(mod->m_title));
m_selected_mod_name->setText(QString::fromStdString(mod->m_mod.m_title));
m_selected_mod_name->setAlignment(Qt::AlignLeft);
QFont font = m_selected_mod_name->font();
font.setWeight(QFont::Bold);
m_selected_mod_name->setFont(font);
if (!mod->m_author.empty())
if (!mod->m_mod.m_author.empty())
{
auto* author_label = new QLabel(tr("By: %1").arg(QString::fromStdString(mod->m_author)));
auto* author_label = new QLabel(tr("By: %1").arg(QString::fromStdString(mod->m_mod.m_author)));
m_mod_meta_layout->addWidget(author_label);
}
if (!mod->m_description.empty())
if (!mod->m_mod.m_description.empty())
{
auto* description_label =
new QLabel(tr("Description: %1").arg(QString::fromStdString(mod->m_description)));
new QLabel(tr("Description: %1").arg(QString::fromStdString(mod->m_mod.m_description)));
description_label->setWordWrap(true);
m_mod_meta_layout->addWidget(description_label);
}
@ -227,11 +212,9 @@ void GraphicsModListWidget::SaveModList()
{
for (int i = 0; i < m_mod_list->count(); i++)
{
const auto absolute_path = m_mod_list->model()
->data(m_mod_list->model()->index(i, 0), Qt::UserRole)
.toString()
.toStdString();
m_mod_group.GetMod(absolute_path)->m_weight = i;
const auto id =
m_mod_list->model()->data(m_mod_list->model()->index(i, 0), Qt::UserRole).toULongLong();
m_mod_group.GetMod(id)->m_weight = i;
}
if (m_loaded_game_is_running)
@ -247,7 +230,8 @@ void GraphicsModListWidget::SaveToDisk()
m_mod_group.Save();
}
const GraphicsModGroupConfig& GraphicsModListWidget::GetGraphicsModConfig() const
const GraphicsModSystem::Config::GraphicsModGroup&
GraphicsModListWidget::GetGraphicsModConfig() const
{
return m_mod_group;
}

View file

@ -39,7 +39,7 @@ public:
void SaveToDisk();
const GraphicsModGroupConfig& GetGraphicsModConfig() const;
const GraphicsModSystem::Config::GraphicsModGroup& GetGraphicsModConfig() const;
signals:
void OpenGraphicsSettings();
@ -52,7 +52,7 @@ private:
void ModSelectionChanged();
void ModItemChanged(QListWidgetItem* item);
void OnModChanged(const std::optional<std::string>& absolute_path);
void OnModChanged(u64 id);
void SaveModList();
@ -72,5 +72,5 @@ private:
GraphicsModWarningWidget* m_warning;
std::string m_game_id;
GraphicsModGroupConfig m_mod_group;
GraphicsModSystem::Config::GraphicsModGroup m_mod_group;
};

View file

@ -574,12 +574,12 @@ void RenderWidget::PassEventToPresenter(const QEvent* event)
void RenderWidget::SetPresenterKeyMap()
{
static constexpr DolphinKeyMap key_map = {
Qt::Key_Tab, Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down,
Qt::Key_PageUp, Qt::Key_PageDown, Qt::Key_Home, Qt::Key_End, Qt::Key_Insert,
Qt::Key_Delete, Qt::Key_Backspace, Qt::Key_Space, Qt::Key_Return, Qt::Key_Escape,
Qt::Key_Tab, Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down,
Qt::Key_PageUp, Qt::Key_PageDown, Qt::Key_Home, Qt::Key_End, Qt::Key_Insert,
Qt::Key_Delete, Qt::Key_Backspace, Qt::Key_Space, Qt::Key_Return, Qt::Key_Escape,
Qt::Key_Enter, // Keypad enter
Qt::Key_A, Qt::Key_C, Qt::Key_V, Qt::Key_X, Qt::Key_Y,
Qt::Key_Z,
Qt::Key_Control, Qt::Key_Shift, Qt::Key_A, Qt::Key_C, Qt::Key_V,
Qt::Key_X, Qt::Key_Y, Qt::Key_Z,
};
g_presenter->SetKeyMap(key_map);

View file

@ -77,6 +77,7 @@ static void CreateDumpPath(std::string path)
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_IDX));
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX));
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX));
File::CreateFullPath(File::GetUserPath(D_DUMPMESHES_IDX));
}
static void CreateLoadPath(std::string path)

View file

@ -213,6 +213,7 @@ void VertexManager::ResetBuffer(u32 vertex_stride)
m_cur_buffer_pointer = m_base_buffer_pointer;
m_end_buffer_pointer = m_base_buffer_pointer + m_cpu_vertex_buffer.size();
m_index_generator.Start(m_cpu_index_buffer.data());
m_last_reset_pointer = m_cur_buffer_pointer;
}
void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_indices,

View file

@ -111,6 +111,7 @@ void VertexManager::ResetBuffer(u32 vertex_stride)
m_end_buffer_pointer = m_vertex_stream_buffer.GetCurrentHostPointer() + MAXVBUFFERSIZE;
m_cur_buffer_pointer = m_vertex_stream_buffer.GetCurrentHostPointer();
m_index_generator.Start(reinterpret_cast<u16*>(m_index_stream_buffer.GetCurrentHostPointer()));
m_last_reset_pointer = m_cur_buffer_pointer;
}
void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_indices,

View file

@ -67,6 +67,7 @@ void Metal::VertexManager::ResetBuffer(u32 vertex_stride)
m_vertex_offset = m_base_vertex * vertex_stride - vertex.second;
m_cur_buffer_pointer = m_base_buffer_pointer = static_cast<u8*>(vertex.first) + m_vertex_offset;
m_end_buffer_pointer = m_base_buffer_pointer + max_vertex_size;
m_last_reset_pointer = m_cur_buffer_pointer;
m_index_generator.Start(static_cast<u16*>(index.first));
}

View file

@ -166,6 +166,7 @@ void VertexManager::ResetBuffer(u32 vertex_stride)
auto buffer = m_vertex_buffer->Map(MAXVBUFFERSIZE, vertex_stride);
m_cur_buffer_pointer = m_base_buffer_pointer = buffer.first;
m_end_buffer_pointer = buffer.first + MAXVBUFFERSIZE;
m_last_reset_pointer = m_cur_buffer_pointer;
buffer = m_index_buffer->Map(MAXIBUFFERSIZE * sizeof(u16));
m_index_generator.Start(reinterpret_cast<u16*>(buffer.first));

View file

@ -171,6 +171,7 @@ void VertexManager::ResetBuffer(u32 vertex_stride)
m_end_buffer_pointer = m_vertex_stream_buffer->GetCurrentHostPointer() + MAXVBUFFERSIZE;
m_cur_buffer_pointer = m_vertex_stream_buffer->GetCurrentHostPointer();
m_index_generator.Start(reinterpret_cast<u16*>(m_index_stream_buffer->GetCurrentHostPointer()));
m_last_reset_pointer = m_cur_buffer_pointer;
}
void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_indices,

View file

@ -6,8 +6,8 @@
namespace VideoCommon
{
CustomAsset::CustomAsset(std::shared_ptr<CustomAssetLibrary> library,
const CustomAssetLibrary::AssetID& asset_id)
: m_owning_library(std::move(library)), m_asset_id(asset_id)
const CustomAssetLibrary::AssetID& asset_id, u64 session_id)
: m_owning_library(std::move(library)), m_asset_id(asset_id), m_session_id(session_id)
{
}
@ -34,6 +34,11 @@ const CustomAssetLibrary::TimeType& CustomAsset::GetLastLoadedTime() const
return m_last_loaded_time;
}
std::size_t CustomAsset::GetSessionId() const
{
return m_session_id;
}
const CustomAssetLibrary::AssetID& CustomAsset::GetAssetId() const
{
return m_asset_id;

View file

@ -18,7 +18,7 @@ class CustomAsset
{
public:
CustomAsset(std::shared_ptr<CustomAssetLibrary> library,
const CustomAssetLibrary::AssetID& asset_id);
const CustomAssetLibrary::AssetID& asset_id, u64 session_id);
virtual ~CustomAsset() = default;
CustomAsset(const CustomAsset&) = delete;
CustomAsset(CustomAsset&&) = delete;
@ -39,6 +39,9 @@ public:
// Returns an id that uniquely identifies this asset
const CustomAssetLibrary::AssetID& GetAssetId() const;
// Returns an id that is unique to this session
std::size_t GetSessionId() const;
// A rough estimate of how much space this asset
// will take in memroy
std::size_t GetByteSizeInMemory() const;
@ -49,6 +52,7 @@ protected:
private:
virtual CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) = 0;
CustomAssetLibrary::AssetID m_asset_id;
std::size_t m_session_id;
mutable std::mutex m_info_lock;
std::size_t m_bytes_loaded = 0;

View file

@ -13,6 +13,9 @@ namespace VideoCommon
struct MaterialData;
struct MeshData;
struct PixelShaderData;
struct RasterMaterialData;
struct RasterShaderData;
struct RenderTargetData;
struct TextureData;
// This class provides functionality to load
@ -46,11 +49,16 @@ public:
// Loads a pixel shader
virtual LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) = 0;
virtual LoadInfo LoadShader(const AssetID& asset_id, RasterShaderData* data) = 0;
// Loads a material
virtual LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) = 0;
virtual LoadInfo LoadMaterial(const AssetID& asset_id, RasterMaterialData* data) = 0;
// Loads a mesh
virtual LoadInfo LoadMesh(const AssetID& asset_id, MeshData* data) = 0;
// Loads a render target
virtual LoadInfo LoadRenderTarget(const AssetID& asset_id, RenderTargetData* data) = 0;
};
} // namespace VideoCommon

View file

@ -4,13 +4,17 @@
#include "VideoCommon/Assets/CustomAssetLoader.h"
#include "Common/MemoryUtil.h"
#include "Common/Thread.h"
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/VideoEvents.h"
namespace VideoCommon
{
void CustomAssetLoader::Init()
{
m_asset_monitor_thread_shutdown.Clear();
m_frame_event =
AfterFrameEvent::Register([this](Core::System&) { OnFrameEnd(); }, "CustomAssetLoader");
const size_t sys_mem = Common::MemPhysical();
const size_t recommended_min_mem = 2 * size_t(1024 * 1024 * 1024);
@ -18,65 +22,12 @@ void CustomAssetLoader::Init()
m_max_memory_available =
(sys_mem / 2 < recommended_min_mem) ? (sys_mem / 2) : (sys_mem - recommended_min_mem);
m_asset_monitor_thread = std::thread([this]() {
Common::SetCurrentThreadName("Asset monitor");
while (true)
{
if (m_asset_monitor_thread_shutdown.IsSet())
{
break;
}
std::this_thread::sleep_for(TIME_BETWEEN_ASSET_MONITOR_CHECKS);
std::lock_guard lk(m_asset_load_lock);
for (auto& [asset_id, asset_to_monitor] : m_assets_to_monitor)
{
if (auto ptr = asset_to_monitor.lock())
{
const auto write_time = ptr->GetLastWriteTime();
if (write_time > ptr->GetLastLoadedTime())
{
(void)ptr->Load();
}
}
}
}
});
m_asset_load_thread.Reset("Custom Asset Loader", [this](std::weak_ptr<CustomAsset> asset) {
if (auto ptr = asset.lock())
{
if (m_memory_exceeded)
return;
if (ptr->Load())
{
std::lock_guard lk(m_asset_load_lock);
const std::size_t asset_memory_size = ptr->GetByteSizeInMemory();
m_total_bytes_loaded += asset_memory_size;
m_assets_to_monitor.try_emplace(ptr->GetAssetId(), ptr);
if (m_total_bytes_loaded > m_max_memory_available)
{
ERROR_LOG_FMT(VIDEO,
"Asset memory exceeded with asset '{}', future assets won't load until "
"memory is available.",
ptr->GetAssetId());
m_memory_exceeded = true;
}
}
}
});
ResizeWorkerThreads(2);
}
void CustomAssetLoader ::Shutdown()
{
m_asset_load_thread.Shutdown(true);
m_asset_monitor_thread_shutdown.Set();
m_asset_monitor_thread.join();
m_assets_to_monitor.clear();
m_total_bytes_loaded = 0;
Reset(false);
}
std::shared_ptr<GameTextureAsset>
@ -105,4 +56,233 @@ std::shared_ptr<MeshAsset> CustomAssetLoader::LoadMesh(const CustomAssetLibrary:
{
return LoadOrCreateAsset<MeshAsset>(asset_id, m_meshes, std::move(library));
}
void CustomAssetLoader::AssetReferenced(u64 asset_session_id)
{
auto& asset_load_info = m_asset_load_info[asset_session_id];
asset_load_info.last_xfb_seen = m_xfbs_seen;
}
void CustomAssetLoader::Reset(bool restart_worker_threads)
{
const std::size_t worker_thread_count = m_worker_threads.size();
StopWorkerThreads();
m_game_textures.clear();
m_pixel_shaders.clear();
m_materials.clear();
m_meshes.clear();
m_assetid_to_asset_index.clear();
m_asset_load_info.clear();
{
std::lock_guard<std::mutex> guard(m_reload_work_lock);
m_assetids_to_reload.clear();
}
{
std::lock_guard<std::mutex> guard(m_pending_work_lock);
m_pending_work_per_frame.clear();
}
{
std::lock_guard<std::mutex> guard(m_completed_work_lock);
m_completed_work.clear();
}
if (restart_worker_threads)
{
StartWorkerThreads(static_cast<u32>(worker_thread_count));
}
}
void CustomAssetLoader::ReloadAsset(const CustomAssetLibrary::AssetID& asset_id)
{
std::lock_guard<std::mutex> guard(m_reload_work_lock);
m_assetids_to_reload.push_back(asset_id);
}
bool CustomAssetLoader::StartWorkerThreads(u32 num_worker_threads)
{
if (num_worker_threads == 0)
return true;
for (u32 i = 0; i < num_worker_threads; i++)
{
m_worker_thread_start_result.store(false);
void* thread_param = nullptr;
std::thread thr(&CustomAssetLoader::WorkerThreadEntryPoint, this, thread_param);
m_init_event.Wait();
if (!m_worker_thread_start_result.load())
{
WARN_LOG_FMT(VIDEO, "Failed to start asset load worker thread.");
thr.join();
break;
}
m_worker_threads.push_back(std::move(thr));
}
return HasWorkerThreads();
}
bool CustomAssetLoader::ResizeWorkerThreads(u32 num_worker_threads)
{
if (m_worker_threads.size() == num_worker_threads)
return true;
StopWorkerThreads();
return StartWorkerThreads(num_worker_threads);
}
bool CustomAssetLoader::HasWorkerThreads() const
{
return !m_worker_threads.empty();
}
void CustomAssetLoader::StopWorkerThreads()
{
if (!HasWorkerThreads())
return;
// Signal worker threads to stop, and wake all of them.
{
std::lock_guard<std::mutex> guard(m_pending_work_lock);
m_exit_flag.Set();
m_worker_thread_wake.notify_all();
}
// Wait for worker threads to exit.
for (std::thread& thr : m_worker_threads)
thr.join();
m_worker_threads.clear();
m_exit_flag.Clear();
}
void CustomAssetLoader::WorkerThreadEntryPoint(void* param)
{
Common::SetCurrentThreadName("Asset Loader Worker");
m_worker_thread_start_result.store(true);
m_init_event.Set();
WorkerThreadRun();
}
void CustomAssetLoader::WorkerThreadRun()
{
std::unique_lock<std::mutex> pending_lock(m_pending_work_lock);
while (!m_exit_flag.IsSet())
{
m_worker_thread_wake.wait(pending_lock);
while (!m_pending_work_per_frame.empty() && !m_exit_flag.IsSet())
{
m_busy_workers++;
auto pending_iter = m_pending_work_per_frame.begin();
auto item(std::move(pending_iter->second));
m_pending_work_per_frame.erase(pending_iter);
const auto item_shared = item.lock();
pending_lock.unlock();
if (item_shared)
{
if (item_shared->Load())
{
std::lock_guard<std::mutex> completed_guard(m_completed_work_lock);
m_completed_work.push_back(item_shared->GetSessionId());
}
}
pending_lock.lock();
m_busy_workers--;
}
}
}
void CustomAssetLoader::OnFrameEnd()
{
std::vector<CustomAssetLibrary::AssetID> assetids_to_reload;
{
std::lock_guard<std::mutex> guard(m_reload_work_lock);
m_assetids_to_reload.swap(assetids_to_reload);
}
for (const CustomAssetLibrary::AssetID& asset_id : assetids_to_reload)
{
if (const auto asset_session_id_iter = m_assetid_to_asset_index.find(asset_id);
asset_session_id_iter != m_assetid_to_asset_index.end())
{
auto& asset_load_info = m_asset_load_info[asset_session_id_iter->second];
asset_load_info.xfb_load_request = m_xfbs_seen;
}
}
std::vector<std::size_t> completed_work;
{
std::lock_guard<std::mutex> guard(m_completed_work_lock);
m_completed_work.swap(completed_work);
}
for (std::size_t completed_index : completed_work)
{
auto& asset_load_info = m_asset_load_info[completed_index];
// If we had a load request and it wasn't from this frame, clear it
if (asset_load_info.xfb_load_request && asset_load_info.xfb_load_request != m_xfbs_seen)
{
asset_load_info.xfb_load_request = std::nullopt;
}
}
m_xfbs_seen++;
std::size_t total_bytes_loaded = 0;
// Build up the work prioritizing newest requested assets first
PendingWorkContainer new_pending_work;
for (const auto& asset_load_info : m_asset_load_info)
{
if (const auto asset = asset_load_info.asset.lock())
{
total_bytes_loaded += asset->GetByteSizeInMemory();
if (total_bytes_loaded > m_max_memory_available)
{
if (!m_memory_exceeded)
{
m_memory_exceeded = true;
ERROR_LOG_FMT(VIDEO,
"Asset memory exceeded with asset '{}', future assets won't load until "
"memory is available.",
asset->GetAssetId());
}
break;
}
if (asset_load_info.xfb_load_request)
{
new_pending_work.emplace(asset_load_info.last_xfb_seen, asset_load_info.asset);
}
}
}
if (m_memory_exceeded && total_bytes_loaded <= m_max_memory_available)
{
INFO_LOG_FMT(VIDEO, "Asset memory went below limit, new assets can begin loading.");
m_memory_exceeded = false;
}
if (new_pending_work.empty())
return;
// Now notify our workers
{
std::lock_guard<std::mutex> guard(m_pending_work_lock);
std::swap(m_pending_work_per_frame, new_pending_work);
m_worker_thread_wake.notify_all();
}
}
} // namespace VideoCommon

View file

@ -4,19 +4,24 @@
#pragma once
#include <chrono>
#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <vector>
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Common/HookableEvent.h"
#include "Common/Logging/Log.h"
#include "Common/WorkQueueThread.h"
#include "VideoCommon/Assets/CustomAsset.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/MeshAsset.h"
#include "VideoCommon/Assets/ShaderAsset.h"
#include "VideoCommon/Assets/TextureAsset.h"
#include "VideoCommon/Present.h"
namespace VideoCommon
{
@ -52,6 +57,14 @@ public:
std::shared_ptr<MeshAsset> LoadMesh(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<CustomAssetLibrary> library);
// Notifies the asset system that an asset has been used
void AssetReferenced(u64 asset_session_id);
// Requests that an asset that exists be reloaded
void ReloadAsset(const CustomAssetLibrary::AssetID& asset_id);
void Reset(bool restart_worker_threads = true);
private:
// TODO C++20: use a 'derived_from' concept against 'CustomAsset' when available
template <typename AssetType>
@ -65,44 +78,87 @@ private:
{
auto shared = it->second.lock();
if (shared)
return shared;
}
std::shared_ptr<AssetType> ptr(new AssetType(std::move(library), asset_id), [&](AssetType* a) {
{
std::lock_guard lk(m_asset_load_lock);
m_total_bytes_loaded -= a->GetByteSizeInMemory();
m_assets_to_monitor.erase(a->GetAssetId());
if (m_max_memory_available >= m_total_bytes_loaded && m_memory_exceeded)
{
INFO_LOG_FMT(VIDEO, "Asset memory went below limit, new assets can begin loading.");
m_memory_exceeded = false;
}
auto& asset_load_info = m_asset_load_info[shared->GetSessionId()];
asset_load_info.last_xfb_seen = m_xfbs_seen;
asset_load_info.xfb_load_request = m_xfbs_seen;
return shared;
}
delete a;
});
}
const auto [index_it, index_inserted] = m_assetid_to_asset_index.try_emplace(asset_id, 0);
if (index_inserted)
{
index_it->second = m_asset_load_info.size();
}
auto ptr = std::make_shared<AssetType>(std::move(library), asset_id, index_it->second);
it->second = ptr;
m_asset_load_thread.Push(it->second);
AssetLoadInfo* asset_load_info;
if (index_inserted)
{
m_asset_load_info.emplace_back();
asset_load_info = &m_asset_load_info.back();
}
else
{
asset_load_info = &m_asset_load_info[index_it->second];
}
asset_load_info->asset = ptr;
asset_load_info->last_xfb_seen = m_xfbs_seen;
asset_load_info->xfb_load_request = m_xfbs_seen;
return ptr;
}
static constexpr auto TIME_BETWEEN_ASSET_MONITOR_CHECKS = std::chrono::milliseconds{500};
bool StartWorkerThreads(u32 num_worker_threads);
bool ResizeWorkerThreads(u32 num_worker_threads);
bool HasWorkerThreads() const;
void StopWorkerThreads();
void WorkerThreadEntryPoint(void* param);
void WorkerThreadRun();
void OnFrameEnd();
Common::EventHook m_frame_event;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<GameTextureAsset>> m_game_textures;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<PixelShaderAsset>> m_pixel_shaders;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<MaterialAsset>> m_materials;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<MeshAsset>> m_meshes;
std::thread m_asset_monitor_thread;
Common::Flag m_asset_monitor_thread_shutdown;
std::size_t m_total_bytes_loaded = 0;
std::map<CustomAssetLibrary::AssetID, std::size_t> m_assetid_to_asset_index;
struct AssetLoadInfo
{
std::weak_ptr<CustomAsset> asset;
u64 last_xfb_seen = 0;
std::optional<u64> xfb_load_request;
};
std::vector<AssetLoadInfo> m_asset_load_info;
u64 m_xfbs_seen = 0;
std::size_t m_max_memory_available = 0;
std::atomic_bool m_memory_exceeded = false;
bool m_memory_exceeded = false;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<CustomAsset>> m_assets_to_monitor;
Common::Flag m_exit_flag;
Common::Event m_init_event;
// Use a recursive mutex to handle the scenario where an asset goes out of scope while
// iterating over the assets to monitor which calls the lock above in 'LoadOrCreateAsset'
std::recursive_mutex m_asset_load_lock;
Common::WorkQueueThread<std::weak_ptr<CustomAsset>> m_asset_load_thread;
std::vector<std::thread> m_worker_threads;
std::atomic_bool m_worker_thread_start_result{false};
using PendingWorkContainer = std::multimap<u64, std::weak_ptr<CustomAsset>, std::greater<>>;
PendingWorkContainer m_pending_work_per_frame;
std::mutex m_pending_work_lock;
std::condition_variable m_worker_thread_wake;
std::atomic_size_t m_busy_workers{0};
std::vector<std::size_t> m_completed_work;
std::mutex m_completed_work_lock;
std::vector<CustomAssetLibrary::AssetID> m_assetids_to_reload;
std::mutex m_reload_work_lock;
};
} // namespace VideoCommon

View file

@ -0,0 +1,175 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/Assets/CustomAssetLoader2.h"
#include "Common/Logging/Log.h"
#include "Common/Thread.h"
namespace VideoCommon
{
void CustomAssetLoader2::Initialize()
{
ResizeWorkerThreads(2);
}
void CustomAssetLoader2 ::Shutdown()
{
Reset(false);
}
void CustomAssetLoader2::Reset(bool restart_worker_threads)
{
const std::size_t worker_thread_count = m_worker_threads.size();
StopWorkerThreads();
{
std::lock_guard<std::mutex> guard(m_pending_work_lock);
m_pending_assets.clear();
m_max_memory_allowed = 0;
m_current_asset_memory = 0;
}
{
std::lock_guard<std::mutex> guard(m_completed_work_lock);
m_completed_asset_session_ids.clear();
m_completed_asset_memory = 0;
}
if (restart_worker_threads)
{
StartWorkerThreads(static_cast<u32>(worker_thread_count));
}
}
bool CustomAssetLoader2::StartWorkerThreads(u32 num_worker_threads)
{
if (num_worker_threads == 0)
return true;
for (u32 i = 0; i < num_worker_threads; i++)
{
m_worker_thread_start_result.store(false);
void* thread_param = nullptr;
std::thread thr(&CustomAssetLoader2::WorkerThreadEntryPoint, this, thread_param);
m_init_event.Wait();
if (!m_worker_thread_start_result.load())
{
WARN_LOG_FMT(VIDEO, "Failed to start asset load worker thread.");
thr.join();
break;
}
m_worker_threads.push_back(std::move(thr));
}
return HasWorkerThreads();
}
bool CustomAssetLoader2::ResizeWorkerThreads(u32 num_worker_threads)
{
if (m_worker_threads.size() == num_worker_threads)
return true;
StopWorkerThreads();
return StartWorkerThreads(num_worker_threads);
}
bool CustomAssetLoader2::HasWorkerThreads() const
{
return !m_worker_threads.empty();
}
void CustomAssetLoader2::StopWorkerThreads()
{
if (!HasWorkerThreads())
return;
// Signal worker threads to stop, and wake all of them.
{
std::lock_guard<std::mutex> guard(m_pending_work_lock);
m_exit_flag.Set();
m_worker_thread_wake.notify_all();
}
// Wait for worker threads to exit.
for (std::thread& thr : m_worker_threads)
thr.join();
m_worker_threads.clear();
m_exit_flag.Clear();
}
void CustomAssetLoader2::WorkerThreadEntryPoint(void* param)
{
Common::SetCurrentThreadName("Asset Loader Worker");
m_worker_thread_start_result.store(true);
m_init_event.Set();
WorkerThreadRun();
}
void CustomAssetLoader2::WorkerThreadRun()
{
std::unique_lock<std::mutex> pending_lock(m_pending_work_lock);
while (!m_exit_flag.IsSet())
{
m_worker_thread_wake.wait(pending_lock);
while (!m_pending_assets.empty() && !m_exit_flag.IsSet())
{
auto pending_iter = m_pending_assets.begin();
const auto item = *pending_iter;
m_pending_assets.erase(pending_iter);
if ((m_current_asset_memory + m_completed_asset_memory) > m_max_memory_allowed)
break;
pending_lock.unlock();
if (item->Load())
{
std::lock_guard<std::mutex> completed_guard(m_completed_work_lock);
m_completed_asset_memory += item->GetByteSizeInMemory();
m_completed_asset_session_ids.push_back(item->GetSessionId());
}
pending_lock.lock();
}
}
}
std::vector<std::size_t>
CustomAssetLoader2::LoadAssets(const std::list<CustomAsset*>& pending_assets,
u64 current_loaded_memory, u64 max_memory_allowed)
{
u64 total_memory = current_loaded_memory;
std::vector<std::size_t> completed_asset_session_ids;
{
std::lock_guard<std::mutex> guard(m_completed_work_lock);
m_completed_asset_session_ids.swap(completed_asset_session_ids);
total_memory += m_completed_asset_memory;
m_completed_asset_memory = 0;
}
if (pending_assets.empty())
return completed_asset_session_ids;
if (total_memory > max_memory_allowed)
return completed_asset_session_ids;
// There's new assets to process, notify worker threads
{
std::lock_guard<std::mutex> guard(m_pending_work_lock);
m_pending_assets = pending_assets;
m_current_asset_memory = total_memory;
m_max_memory_allowed = max_memory_allowed;
if (m_current_asset_memory < m_max_memory_allowed)
m_worker_thread_wake.notify_all();
}
return completed_asset_session_ids;
}
} // namespace VideoCommon

View file

@ -0,0 +1,65 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <condition_variable>
#include <list>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#include "Common/Event.h"
#include "Common/Flag.h"
#include "VideoCommon/Assets/CustomAsset.h"
namespace VideoCommon
{
class CustomAssetLoader2
{
public:
CustomAssetLoader2() = default;
~CustomAssetLoader2() = default;
CustomAssetLoader2(const CustomAssetLoader2&) = delete;
CustomAssetLoader2(CustomAssetLoader2&&) = delete;
CustomAssetLoader2& operator=(const CustomAssetLoader2&) = delete;
CustomAssetLoader2& operator=(CustomAssetLoader2&&) = delete;
void Initialize();
void Shutdown();
// Returns a vector of asset session ids that were loaded in the last frame
std::vector<std::size_t> LoadAssets(const std::list<CustomAsset*>& pending_assets,
u64 current_loaded_memory, u64 max_memory_allowed);
void Reset(bool restart_worker_threads = true);
private:
bool StartWorkerThreads(u32 num_worker_threads);
bool ResizeWorkerThreads(u32 num_worker_threads);
bool HasWorkerThreads() const;
void StopWorkerThreads();
void WorkerThreadEntryPoint(void* param);
void WorkerThreadRun();
Common::Flag m_exit_flag;
Common::Event m_init_event;
std::vector<std::thread> m_worker_threads;
std::atomic_bool m_worker_thread_start_result{false};
std::list<CustomAsset*> m_pending_assets;
std::atomic<u64> m_current_asset_memory = 0;
u64 m_max_memory_allowed = 0;
std::mutex m_pending_work_lock;
std::condition_variable m_worker_thread_wake;
std::vector<std::size_t> m_completed_asset_session_ids;
std::atomic<u64> m_completed_asset_memory = 0;
std::mutex m_completed_work_lock;
};
} // namespace VideoCommon

View file

@ -13,10 +13,13 @@
#include "Common/JsonUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/System.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/MeshAsset.h"
#include "VideoCommon/Assets/RenderTargetAsset.h"
#include "VideoCommon/Assets/ShaderAsset.h"
#include "VideoCommon/Assets/TextureAsset.h"
#include "VideoCommon/GraphicsModSystem/Runtime/CustomResourceManager.h"
#include "VideoCommon/RenderState.h"
namespace VideoCommon
@ -53,7 +56,7 @@ std::size_t GetAssetSize(const CustomTextureData& data)
CustomAssetLibrary::TimeType
DirectFilesystemAssetLibrary::GetLastAssetWriteTime(const AssetID& asset_id) const
{
std::lock_guard lk(m_lock);
std::lock_guard lk(m_asset_map_lock);
if (auto iter = m_assetid_to_asset_map_path.find(asset_id);
iter != m_assetid_to_asset_map_path.end())
{
@ -161,6 +164,118 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadPixelShader(const
return LoadInfo{approx_mem_size, GetLastAssetWriteTime(asset_id)};
}
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadShader(const AssetID& asset_id,
RasterShaderData* data)
{
const auto asset_map = GetAssetMapForID(asset_id);
// Asset map for a pixel shader is the shader and some metadata
if (asset_map.size() != 3)
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have three files mapped!", asset_id);
return {};
}
const auto metadata = asset_map.find("metadata");
if (metadata == asset_map.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a metadata entry mapped!", asset_id);
return {};
}
const auto vertex_shader = asset_map.find("vertex_shader");
if (vertex_shader == asset_map.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a vertex shader entry mapped!", asset_id);
return {};
}
const auto pixel_shader = asset_map.find("pixel_shader");
if (pixel_shader == asset_map.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a pixel shader entry mapped!", asset_id);
return {};
}
std::size_t metadata_size;
{
std::error_code ec;
metadata_size = std::filesystem::file_size(metadata->second, ec);
if (ec)
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - failed to get shader metadata file size with error '{}'!",
asset_id, ec);
return {};
}
}
std::size_t vertex_shader_size;
{
std::error_code ec;
vertex_shader_size = std::filesystem::file_size(vertex_shader->second, ec);
if (ec)
{
ERROR_LOG_FMT(
VIDEO, "Asset '{}' error - failed to get vertex shader source file size with error '{}'!",
asset_id, ec);
return {};
}
}
std::size_t pixel_shader_size;
{
std::error_code ec;
pixel_shader_size = std::filesystem::file_size(pixel_shader->second, ec);
if (ec)
{
ERROR_LOG_FMT(
VIDEO, "Asset '{}' error - failed to get pixel shader source file size with error '{}'!",
asset_id, ec);
return {};
}
}
const auto approx_mem_size = metadata_size + vertex_shader_size + pixel_shader_size;
if (!File::ReadFileToString(PathToString(vertex_shader->second), data->m_vertex_source))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the vertex shader file '{}',",
asset_id, PathToString(vertex_shader->second));
return {};
}
if (!File::ReadFileToString(PathToString(pixel_shader->second), data->m_pixel_source))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the pixel shader file '{}',", asset_id,
PathToString(pixel_shader->second));
return {};
}
picojson::value root;
std::string error;
if (!JsonFromFile(PathToString(metadata->second), &root, &error))
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - failed to load the json file '{}', due to parse error: {}",
asset_id, PathToString(metadata->second), error);
return {};
}
if (!root.is<picojson::object>())
{
ERROR_LOG_FMT(
VIDEO,
"Asset '{}' error - failed to load the json file '{}', due to root not being an object!",
asset_id, PathToString(metadata->second));
return {};
}
const auto& root_obj = root.get<picojson::object>();
if (!RasterShaderData::FromJson(asset_id, root_obj, data))
return {};
return LoadInfo{approx_mem_size, GetLastAssetWriteTime(asset_id)};
}
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMaterial(const AssetID& asset_id,
MaterialData* data)
{
@ -219,6 +334,64 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMaterial(const As
return LoadInfo{metadata_size, GetLastAssetWriteTime(asset_id)};
}
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMaterial(const AssetID& asset_id,
RasterMaterialData* data)
{
const auto asset_map = GetAssetMapForID(asset_id);
// Material is expected to have one asset mapped
if (asset_map.empty() || asset_map.size() > 1)
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - material expected to have one file mapped!", asset_id);
return {};
}
const auto& asset_path = asset_map.begin()->second;
std::size_t metadata_size;
{
std::error_code ec;
metadata_size = std::filesystem::file_size(asset_path, ec);
if (ec)
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to get material file size with error '{}'!",
asset_id, ec);
return {};
}
}
picojson::value root;
std::string error;
if (!JsonFromFile(PathToString(asset_path), &root, &error))
{
ERROR_LOG_FMT(
VIDEO,
"Asset '{}' error - material failed to load the json file '{}', due to parse error: {}",
asset_id, PathToString(asset_path), error);
return {};
}
if (!root.is<picojson::object>())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - material failed to load the json file '{}', due to root not "
"being an object!",
asset_id, PathToString(asset_path));
return {};
}
const auto& root_obj = root.get<picojson::object>();
if (!RasterMaterialData::FromJson(asset_id, root_obj, data))
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - material failed to load the json file '{}', as material "
"json could not be parsed!",
asset_id, PathToString(asset_path));
return {};
}
return LoadInfo{metadata_size, GetLastAssetWriteTime(asset_id)};
}
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMesh(const AssetID& asset_id,
MeshData* data)
{
@ -314,6 +487,68 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMesh(const AssetI
return LoadInfo{approx_mem_size, GetLastAssetWriteTime(asset_id)};
}
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadRenderTarget(const AssetID& asset_id,
RenderTargetData* data)
{
const auto asset_map = GetAssetMapForID(asset_id);
// Material is expected to have one asset mapped
if (asset_map.empty() || asset_map.size() > 1)
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - render target expected to have one file mapped!",
asset_id);
return {};
}
const auto& asset_path = asset_map.begin()->second;
std::size_t metadata_size;
{
std::error_code ec;
metadata_size = std::filesystem::file_size(asset_path, ec);
if (ec)
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - failed to get render target file size with error '{}'!",
asset_id, ec);
return {};
}
}
picojson::value root;
std::string error;
if (!JsonFromFile(PathToString(asset_path), &root, &error))
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - render target failed to load the json file '{}', due to "
"parse error: {}",
asset_id, PathToString(asset_path), error);
return {};
}
if (!root.is<picojson::object>())
{
ERROR_LOG_FMT(
VIDEO,
"Asset '{}' error - render target failed to load the json file '{}', due to root not "
"being an object!",
asset_id, PathToString(asset_path));
return {};
}
const auto& root_obj = root.get<picojson::object>();
if (!RenderTargetData::FromJson(asset_id, root_obj, data))
{
ERROR_LOG_FMT(
VIDEO,
"Asset '{}' error - render target failed to load the json file '{}', as render target "
"json could not be parsed!",
asset_id, PathToString(asset_path));
return {};
}
return LoadInfo{metadata_size, GetLastAssetWriteTime(asset_id)};
}
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const AssetID& asset_id,
TextureData* data)
{
@ -434,10 +669,42 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const Ass
}
void DirectFilesystemAssetLibrary::SetAssetIDMapData(const AssetID& asset_id,
AssetMap asset_path_map)
VideoCommon::Assets::AssetMap asset_path_map)
{
std::lock_guard lk(m_lock);
m_assetid_to_asset_map_path[asset_id] = std::move(asset_path_map);
VideoCommon::Assets::AssetMap previous_asset_map;
{
std::lock_guard lk(m_asset_map_lock);
previous_asset_map = m_assetid_to_asset_map_path[asset_id];
}
{
std::lock_guard lk(m_path_map_lock);
for (const auto& [name, path] : previous_asset_map)
{
m_path_to_asset_id.erase(PathToString(path));
}
for (const auto& [name, path] : asset_path_map)
{
m_path_to_asset_id[PathToString(path)] = asset_id;
}
}
{
std::lock_guard lk(m_asset_map_lock);
m_assetid_to_asset_map_path[asset_id] = std::move(asset_path_map);
}
}
void DirectFilesystemAssetLibrary::PathModified(std::string_view path)
{
std::lock_guard lk(m_path_map_lock);
if (const auto iter = m_path_to_asset_id.find(path); iter != m_path_to_asset_id.end())
{
auto& system = Core::System::GetInstance();
auto& resource_manager = system.GetCustomResourceManager();
resource_manager.ReloadAsset(iter->second);
}
}
bool DirectFilesystemAssetLibrary::LoadMips(const std::filesystem::path& asset_path,
@ -492,10 +759,10 @@ bool DirectFilesystemAssetLibrary::LoadMips(const std::filesystem::path& asset_p
return true;
}
DirectFilesystemAssetLibrary::AssetMap
VideoCommon::Assets::AssetMap
DirectFilesystemAssetLibrary::GetAssetMapForID(const AssetID& asset_id) const
{
std::lock_guard lk(m_lock);
std::lock_guard lk(m_asset_map_lock);
if (auto iter = m_assetid_to_asset_map_path.find(asset_id);
iter != m_assetid_to_asset_map_path.end())
{

View file

@ -8,22 +8,24 @@
#include <mutex>
#include <string>
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/Assets/Types.h"
#include "VideoCommon/Assets/WatchableFilesystemAssetLibrary.h"
namespace VideoCommon
{
// This class implements 'CustomAssetLibrary' and loads any assets
// directly from the filesystem
class DirectFilesystemAssetLibrary final : public CustomAssetLibrary
class DirectFilesystemAssetLibrary final : public WatchableFilesystemAssetLibrary
{
public:
using AssetMap = std::map<std::string, std::filesystem::path>;
LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) override;
LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) override;
LoadInfo LoadShader(const AssetID& asset_id, RasterShaderData* data) override;
LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) override;
LoadInfo LoadMaterial(const AssetID& asset_id, RasterMaterialData* data) override;
LoadInfo LoadMesh(const AssetID& asset_id, MeshData* data) override;
LoadInfo LoadRenderTarget(const AssetID& asset_id, RenderTargetData* data) override;
// Gets the latest time from amongst all the files in the asset map
TimeType GetLastAssetWriteTime(const AssetID& asset_id) const override;
@ -31,16 +33,21 @@ public:
// Assigns the asset id to a map of files, how this map is read is dependent on the data
// For instance, a raw texture would expect the map to have a single entry and load that
// file as the asset. But a model file data might have its data spread across multiple files
void SetAssetIDMapData(const AssetID& asset_id, AssetMap asset_path_map);
void SetAssetIDMapData(const AssetID& asset_id, Assets::AssetMap asset_path_map);
private:
void PathModified(std::string_view path) override;
// Loads additional mip levels into the texture structure until _mip<N> texture is not found
bool LoadMips(const std::filesystem::path& asset_path, CustomTextureData::ArraySlice* data);
// Gets the asset map given an asset id
AssetMap GetAssetMapForID(const AssetID& asset_id) const;
Assets::AssetMap GetAssetMapForID(const AssetID& asset_id) const;
mutable std::mutex m_lock;
std::map<AssetID, std::map<std::string, std::filesystem::path>> m_assetid_to_asset_map_path;
mutable std::mutex m_asset_map_lock;
std::map<AssetID, Assets::AssetMap> m_assetid_to_asset_map_path;
mutable std::mutex m_path_map_lock;
std::map<std::string, AssetID, std::less<>> m_path_to_asset_id;
};
} // namespace VideoCommon

View file

@ -130,7 +130,18 @@ bool ParsePropertyValue(const CustomAssetLibrary::AssetID& asset_id,
{
if (json_value.is<std::string>())
{
*value = json_value.get<std::string>();
TextureSamplerValue texture_sampler_value;
texture_sampler_value.asset = json_value.get<std::string>();
*value = texture_sampler_value;
return true;
}
else if (json_value.is<picojson::object>())
{
auto texture_asset_obj = json_value.get<picojson::object>();
TextureSamplerValue texture_sampler_value;
if (!TextureSamplerValue::FromJson(texture_asset_obj, &texture_sampler_value))
return false;
*value = texture_sampler_value;
return true;
}
}
@ -207,6 +218,164 @@ bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id,
return true;
}
template <typename ElementType, std::size_t ElementCount>
bool ParseNumeric(const CustomAssetLibrary::AssetID& asset_id, const picojson::value& json_value,
MaterialProperty2::Value* value)
{
static_assert(ElementCount <= 4, "Numeric data expected to be four elements or less");
if constexpr (ElementCount == 1)
{
if (!json_value.is<double>())
{
ERROR_LOG_FMT(VIDEO,
"Asset id '{}' material has attribute where "
"a double was expected but not provided.",
asset_id);
return false;
}
*value = static_cast<ElementType>(json_value.get<double>());
}
else
{
if (!json_value.is<picojson::array>())
{
ERROR_LOG_FMT(VIDEO,
"Asset id '{}' material has attribute where "
"an array was expected but not provided.",
asset_id);
return false;
}
const auto json_data = json_value.get<picojson::array>();
if (json_data.size() != ElementCount)
{
ERROR_LOG_FMT(VIDEO,
"Asset id '{}' material has attribute with incorrect number "
"of elements, expected {}",
asset_id, ElementCount);
return false;
}
if (!std::all_of(json_data.begin(), json_data.end(),
[](const picojson::value& v) { return v.is<double>(); }))
{
ERROR_LOG_FMT(VIDEO,
"Asset id '{}' material has attribute where "
"all elements are not of type double.",
asset_id);
return false;
}
std::array<ElementType, ElementCount> data;
for (std::size_t i = 0; i < ElementCount; i++)
{
data[i] = static_cast<ElementType>(json_data[i].get<double>());
}
*value = std::move(data);
}
return true;
}
bool ParsePropertyValue(const CustomAssetLibrary::AssetID& asset_id,
const picojson::value& json_value, std::string_view type,
MaterialProperty2::Value* value)
{
if (type == "int")
{
return ParseNumeric<s32, 1>(asset_id, json_value, value);
}
else if (type == "int2")
{
return ParseNumeric<s32, 2>(asset_id, json_value, value);
}
else if (type == "int3")
{
return ParseNumeric<s32, 3>(asset_id, json_value, value);
}
else if (type == "int4")
{
return ParseNumeric<s32, 4>(asset_id, json_value, value);
}
else if (type == "float")
{
return ParseNumeric<float, 1>(asset_id, json_value, value);
}
else if (type == "float2")
{
return ParseNumeric<float, 2>(asset_id, json_value, value);
}
else if (type == "float3")
{
return ParseNumeric<float, 3>(asset_id, json_value, value);
}
else if (type == "float4")
{
return ParseNumeric<float, 4>(asset_id, json_value, value);
}
else if (type == "bool")
{
if (json_value.is<bool>())
{
*value = json_value.get<bool>();
return true;
}
}
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value is not valid for type '{}'",
asset_id, type);
return false;
}
bool ParseMaterialProperties(const CustomAssetLibrary::AssetID& asset_id,
const picojson::array& values_data,
std::vector<MaterialProperty2>* material_property)
{
for (const auto& value_data : values_data)
{
VideoCommon::MaterialProperty2 property;
if (!value_data.is<picojson::object>())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value is not the right json type",
asset_id);
return false;
}
const auto& value_data_obj = value_data.get<picojson::object>();
const auto type_iter = value_data_obj.find("type");
if (type_iter == value_data_obj.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value entry 'type' not found",
asset_id);
return false;
}
if (!type_iter->second.is<std::string>())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse the json, value entry 'type' is not "
"the right json type",
asset_id);
return false;
}
std::string type = type_iter->second.to_str();
Common::ToLower(&type);
const auto value_iter = value_data_obj.find("value");
if (value_iter != value_data_obj.end())
{
if (!ParsePropertyValue(asset_id, value_iter->second, type, &property.m_value))
{
return false;
}
}
material_property->push_back(std::move(property));
}
return true;
}
} // namespace
void MaterialProperty::WriteToMemory(u8*& buffer, const MaterialProperty& property)
@ -218,8 +387,7 @@ void MaterialProperty::WriteToMemory(u8*& buffer, const MaterialProperty& proper
};
std::visit(
overloaded{
[&](const CustomAssetLibrary::AssetID&) {},
[&](s32 value) { write_memory(&value, sizeof(s32)); },
[&](const TextureSamplerValue&) {}, [&](s32 value) { write_memory(&value, sizeof(s32)); },
[&](const std::array<s32, 2>& value) { write_memory(value.data(), sizeof(s32) * 2); },
[&](const std::array<s32, 3>& value) { write_memory(value.data(), sizeof(s32) * 3); },
[&](const std::array<s32, 4>& value) { write_memory(value.data(), sizeof(s32) * 4); },
@ -234,8 +402,7 @@ void MaterialProperty::WriteToMemory(u8*& buffer, const MaterialProperty& proper
std::size_t MaterialProperty::GetMemorySize(const MaterialProperty& property)
{
std::size_t result = 0;
std::visit(overloaded{[&](const CustomAssetLibrary::AssetID&) {},
[&](s32) { result = MemorySize; },
std::visit(overloaded{[&](const TextureSamplerValue&) {}, [&](s32) { result = MemorySize; },
[&](const std::array<s32, 2>&) { result = MemorySize; },
[&](const std::array<s32, 3>&) { result = MemorySize; },
[&](const std::array<s32, 4>&) { result = MemorySize; },
@ -267,7 +434,7 @@ void MaterialProperty::WriteAsShaderCode(ShaderCode& shader_source,
shader_source.Write("{} {}_padding_{};\n", type, property.m_code_name, i + 1);
}
};
std::visit(overloaded{[&](const CustomAssetLibrary::AssetID&) {},
std::visit(overloaded{[&](const TextureSamplerValue&) {},
[&](s32 value) { write_shader("int", 1); },
[&](const std::array<s32, 2>& value) { write_shader("int", 2); },
[&](const std::array<s32, 3>& value) { write_shader("int", 3); },
@ -280,6 +447,44 @@ void MaterialProperty::WriteAsShaderCode(ShaderCode& shader_source,
property.m_value);
}
void MaterialProperty2::WriteToMemory(u8*& buffer, const MaterialProperty2& property)
{
const auto write_memory = [&](const void* raw_value, std::size_t data_size) {
std::memcpy(buffer, raw_value, data_size);
std::memset(buffer + data_size, 0, MemorySize - data_size);
buffer += MemorySize;
};
std::visit(
overloaded{
[&](s32 value) { write_memory(&value, sizeof(s32)); },
[&](const std::array<s32, 2>& value) { write_memory(value.data(), sizeof(s32) * 2); },
[&](const std::array<s32, 3>& value) { write_memory(value.data(), sizeof(s32) * 3); },
[&](const std::array<s32, 4>& value) { write_memory(value.data(), sizeof(s32) * 4); },
[&](float value) { write_memory(&value, sizeof(float)); },
[&](const std::array<float, 2>& value) { write_memory(value.data(), sizeof(float) * 2); },
[&](const std::array<float, 3>& value) { write_memory(value.data(), sizeof(float) * 3); },
[&](const std::array<float, 4>& value) { write_memory(value.data(), sizeof(float) * 4); },
[&](bool value) { write_memory(&value, sizeof(bool)); }},
property.m_value);
}
std::size_t MaterialProperty2::GetMemorySize(const MaterialProperty2& property)
{
std::size_t result = 0;
std::visit(overloaded{[&](s32 value) { result = MemorySize; },
[&](const std::array<s32, 2>&) { result = MemorySize; },
[&](const std::array<s32, 3>&) { result = MemorySize; },
[&](const std::array<s32, 4>&) { result = MemorySize; },
[&](float) { result = MemorySize; },
[&](const std::array<float, 2>&) { result = MemorySize; },
[&](const std::array<float, 3>&) { result = MemorySize; },
[&](const std::array<float, 4>&) { result = MemorySize; },
[&](bool) { result = MemorySize; }},
property.m_value);
return result;
}
bool MaterialData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
const picojson::object& json, MaterialData* data)
{
@ -331,9 +536,11 @@ void MaterialData::ToJson(picojson::object* obj, const MaterialData& data)
picojson::object json_property;
json_property["code_name"] = picojson::value{property.m_code_name};
std::visit(overloaded{[&](const CustomAssetLibrary::AssetID& value) {
std::visit(overloaded{[&](const TextureSamplerValue& value) {
json_property["type"] = picojson::value{"texture_asset"};
json_property["value"] = picojson::value{value};
picojson::object value_json;
TextureSamplerValue::ToJson(&value_json, value);
json_property["value"] = picojson::value{value_json};
},
[&](s32 value) {
json_property["type"] = picojson::value{"int"};
@ -380,6 +587,296 @@ void MaterialData::ToJson(picojson::object* obj, const MaterialData& data)
json_obj["shader_asset"] = picojson::value{data.shader_asset};
}
bool RasterMaterialData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
const picojson::object& json, RasterMaterialData* data)
{
const auto parse_properties = [&](const char* name,
std::vector<MaterialProperty2>* properties) -> bool {
const auto properties_iter = json.find(name);
if (properties_iter == json.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' not found", asset_id, name);
return false;
}
if (!properties_iter->second.is<picojson::array>())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' is not the right json type",
asset_id, name);
return false;
}
const auto& properties_array = properties_iter->second.get<picojson::array>();
if (!ParseMaterialProperties(asset_id, properties_array, properties))
return false;
return true;
};
if (!parse_properties("vertex_properties", &data->vertex_properties))
return false;
if (!parse_properties("pixel_properties", &data->pixel_properties))
return false;
if (const auto shader_asset = ReadStringFromJson(json, "shader_asset"))
{
data->shader_asset = *shader_asset;
}
else
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, 'shader_asset' not found or wrong type",
asset_id);
return false;
}
if (const auto next_material_asset = ReadStringFromJson(json, "next_material_asset"))
{
data->next_material_asset = *next_material_asset;
}
else
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse json, 'next_material_asset' not found or wrong type",
asset_id);
return false;
}
if (const auto cull_mode = ReadNumericFromJson<CullMode>(json, "cull_mode"))
{
data->cull_mode = *cull_mode;
}
if (const auto depth_state = json.find("depth_state");
depth_state != json.end() && depth_state->second.is<picojson::object>())
{
auto& json_obj = depth_state->second.get<picojson::object>();
DepthState state;
state.testenable = ReadNumericFromJson<u32>(json_obj, "testenable").value_or(0);
state.updateenable = ReadNumericFromJson<u32>(json_obj, "updateenable").value_or(0);
state.func = ReadNumericFromJson<CompareMode>(json_obj, "func").value_or(CompareMode::Never);
data->depth_state = state;
}
if (const auto blending_state = json.find("blending_state");
blending_state != json.end() && blending_state->second.is<picojson::object>())
{
auto& json_obj = blending_state->second.get<picojson::object>();
BlendingState state;
state.blendenable = ReadNumericFromJson<u32>(json_obj, "blendenable").value_or(0);
state.logicopenable = ReadNumericFromJson<u32>(json_obj, "logicopenable").value_or(0);
state.colorupdate = ReadNumericFromJson<u32>(json_obj, "colorupdate").value_or(0);
state.alphaupdate = ReadNumericFromJson<u32>(json_obj, "alphaupdate").value_or(0);
state.subtract = ReadNumericFromJson<u32>(json_obj, "subtract").value_or(0);
state.subtractAlpha = ReadNumericFromJson<u32>(json_obj, "subtractAlpha").value_or(0);
state.usedualsrc = ReadNumericFromJson<u32>(json_obj, "usedualsrc").value_or(0);
state.dstfactor =
ReadNumericFromJson<DstBlendFactor>(json_obj, "dstfactor").value_or(DstBlendFactor::Zero);
state.srcfactor =
ReadNumericFromJson<SrcBlendFactor>(json_obj, "srcfactor").value_or(SrcBlendFactor::Zero);
state.dstfactoralpha = ReadNumericFromJson<DstBlendFactor>(json_obj, "dstfactoralpha")
.value_or(DstBlendFactor::Zero);
state.srcfactoralpha = ReadNumericFromJson<SrcBlendFactor>(json_obj, "srcfactoralpha")
.value_or(SrcBlendFactor::Zero);
state.logicmode = ReadNumericFromJson<LogicOp>(json_obj, "logicmode").value_or(LogicOp::Clear);
data->blending_state = state;
}
const auto pixel_textures_iter = json.find("pixel_textures");
if (pixel_textures_iter == json.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, 'pixel_textures' not found", asset_id);
return false;
}
if (!pixel_textures_iter->second.is<picojson::array>())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse json, 'pixel_textures' is not the right json type",
asset_id);
return false;
}
const auto& pixel_textures_array = pixel_textures_iter->second.get<picojson::array>();
if (!std::ranges::all_of(pixel_textures_array, [](const picojson::value& json_data) {
return json_data.is<picojson::object>();
}))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, 'pixel_textures' must contain objects",
asset_id);
return false;
}
for (const auto& pixel_texture_json : pixel_textures_array)
{
auto& pixel_texture_json_obj = pixel_texture_json.get<picojson::object>();
TextureSamplerValue sampler_value;
if (!TextureSamplerValue::FromJson(pixel_texture_json_obj, &sampler_value))
return false;
data->pixel_textures.push_back(std::move(sampler_value));
}
const auto render_targets_iter = json.find("render_targets");
if (render_targets_iter == json.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, 'render_targets' not found", asset_id);
return false;
}
if (!render_targets_iter->second.is<picojson::array>())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse json, 'render_targets' is not the right json type",
asset_id);
return false;
}
const auto& render_targets_array = render_targets_iter->second.get<picojson::array>();
if (!std::ranges::all_of(render_targets_array, [](const picojson::value& json_data) {
return json_data.is<std::string>();
}))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, 'render_targets' must contain strings",
asset_id);
return false;
}
for (const auto& render_target_json : render_targets_array)
{
std::string render_target_str = render_target_json.to_str();
data->render_targets.push_back(std::move(render_target_str));
}
return true;
}
void RasterMaterialData::ToJson(picojson::object* obj, const RasterMaterialData& data)
{
if (!obj) [[unlikely]]
return;
auto& json_obj = *obj;
const auto add_property = [](picojson::array* json_properties,
const MaterialProperty2& property) {
picojson::object json_property;
std::visit(overloaded{[&](s32 value) {
json_property["type"] = picojson::value{"int"};
json_property["value"] = picojson::value{static_cast<double>(value)};
},
[&](const std::array<s32, 2>& value) {
json_property["type"] = picojson::value{"int2"};
json_property["value"] = picojson::value{ToJsonArray(value)};
},
[&](const std::array<s32, 3>& value) {
json_property["type"] = picojson::value{"int3"};
json_property["value"] = picojson::value{ToJsonArray(value)};
},
[&](const std::array<s32, 4>& value) {
json_property["type"] = picojson::value{"int4"};
json_property["value"] = picojson::value{ToJsonArray(value)};
},
[&](float value) {
json_property["type"] = picojson::value{"float"};
json_property["value"] = picojson::value{static_cast<double>(value)};
},
[&](const std::array<float, 2>& value) {
json_property["type"] = picojson::value{"float2"};
json_property["value"] = picojson::value{ToJsonArray(value)};
},
[&](const std::array<float, 3>& value) {
json_property["type"] = picojson::value{"float3"};
json_property["value"] = picojson::value{ToJsonArray(value)};
},
[&](const std::array<float, 4>& value) {
json_property["type"] = picojson::value{"float4"};
json_property["value"] = picojson::value{ToJsonArray(value)};
},
[&](bool value) {
json_property["type"] = picojson::value{"bool"};
json_property["value"] = picojson::value{value};
}},
property.m_value);
json_properties->emplace_back(std::move(json_property));
};
picojson::array json_vertex_properties;
for (const auto& property : data.vertex_properties)
{
add_property(&json_vertex_properties, property);
}
json_obj.emplace("vertex_properties", std::move(json_vertex_properties));
picojson::array json_pixel_properties;
for (const auto& property : data.pixel_properties)
{
add_property(&json_pixel_properties, property);
}
json_obj.emplace("pixel_properties", std::move(json_pixel_properties));
json_obj.emplace("shader_asset", data.shader_asset);
json_obj.emplace("next_material_asset", data.next_material_asset);
if (data.cull_mode)
{
json_obj.emplace("cull_mode", static_cast<double>(*data.cull_mode));
}
if (data.depth_state)
{
picojson::object depth_state_json;
depth_state_json.emplace("testenable",
static_cast<double>(data.depth_state->testenable.Value()));
depth_state_json.emplace("updateenable",
static_cast<double>(data.depth_state->updateenable.Value()));
depth_state_json.emplace("func", static_cast<double>(data.depth_state->func.Value()));
json_obj.emplace("depth_state", depth_state_json);
}
if (data.blending_state)
{
picojson::object blending_state_json;
blending_state_json.emplace("blendenable",
static_cast<double>(data.blending_state->blendenable.Value()));
blending_state_json.emplace("logicopenable",
static_cast<double>(data.blending_state->logicopenable.Value()));
blending_state_json.emplace("colorupdate",
static_cast<double>(data.blending_state->colorupdate.Value()));
blending_state_json.emplace("alphaupdate",
static_cast<double>(data.blending_state->alphaupdate.Value()));
blending_state_json.emplace("subtract",
static_cast<double>(data.blending_state->subtract.Value()));
blending_state_json.emplace("subtractAlpha",
static_cast<double>(data.blending_state->subtractAlpha.Value()));
blending_state_json.emplace("usedualsrc",
static_cast<double>(data.blending_state->usedualsrc.Value()));
blending_state_json.emplace("dstfactor",
static_cast<double>(data.blending_state->dstfactor.Value()));
blending_state_json.emplace("srcfactor",
static_cast<double>(data.blending_state->srcfactor.Value()));
blending_state_json.emplace("dstfactoralpha",
static_cast<double>(data.blending_state->dstfactoralpha.Value()));
blending_state_json.emplace("srcfactoralpha",
static_cast<double>(data.blending_state->srcfactoralpha.Value()));
blending_state_json.emplace("logicmode",
static_cast<double>(data.blending_state->logicmode.Value()));
json_obj.emplace("blending_state", blending_state_json);
}
picojson::array json_textures;
for (const auto& texture : data.pixel_textures)
{
picojson::object json_texture;
TextureSamplerValue::ToJson(&json_texture, texture);
json_textures.emplace_back(std::move(json_texture));
}
json_obj.emplace("pixel_textures", json_textures);
picojson::array json_render_targets;
for (const auto& render_target : data.render_targets)
{
json_render_targets.emplace_back(render_target);
}
json_obj.emplace("render_targets", json_render_targets);
}
CustomAssetLibrary::LoadInfo MaterialAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
{
auto potential_data = std::make_shared<MaterialData>();
@ -393,4 +890,19 @@ CustomAssetLibrary::LoadInfo MaterialAsset::LoadImpl(const CustomAssetLibrary::A
}
return loaded_info;
}
CustomAssetLibrary::LoadInfo
RasterMaterialAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
{
auto potential_data = std::make_shared<RasterMaterialData>();
const auto loaded_info = m_owning_library->LoadMaterial(asset_id, potential_data.get());
if (loaded_info.m_bytes_loaded == 0)
return {};
{
std::lock_guard lk(m_data_lock);
m_loaded = true;
m_data = std::move(potential_data);
}
return loaded_info;
}
} // namespace VideoCommon

View file

@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <optional>
#include <string>
#include <variant>
#include <vector>
@ -13,6 +14,9 @@
#include "Common/CommonTypes.h"
#include "Common/EnumFormatter.h"
#include "VideoCommon/Assets/CustomAsset.h"
#include "VideoCommon/Assets/TextureSamplerValue.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/RenderState.h"
class ShaderCode;
@ -23,13 +27,23 @@ struct MaterialProperty
static void WriteToMemory(u8*& buffer, const MaterialProperty& property);
static std::size_t GetMemorySize(const MaterialProperty& property);
static void WriteAsShaderCode(ShaderCode& shader_source, const MaterialProperty& property);
using Value = std::variant<CustomAssetLibrary::AssetID, s32, std::array<s32, 2>,
std::array<s32, 3>, std::array<s32, 4>, float, std::array<float, 2>,
std::array<float, 3>, std::array<float, 4>, bool>;
using Value = std::variant<TextureSamplerValue, s32, std::array<s32, 2>, std::array<s32, 3>,
std::array<s32, 4>, float, std::array<float, 2>, std::array<float, 3>,
std::array<float, 4>, bool>;
std::string m_code_name;
Value m_value;
};
struct MaterialProperty2
{
static void WriteToMemory(u8*& buffer, const MaterialProperty2& property);
static std::size_t GetMemorySize(const MaterialProperty2& property);
using Value =
std::variant<s32, std::array<s32, 2>, std::array<s32, 3>, std::array<s32, 4>, float,
std::array<float, 2>, std::array<float, 3>, std::array<float, 4>, bool>;
Value m_value;
};
struct MaterialData
{
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
@ -52,4 +66,31 @@ private:
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
};
struct RasterMaterialData
{
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
RasterMaterialData* data);
static void ToJson(picojson::object* obj, const RasterMaterialData& data);
CustomAssetLibrary::AssetID shader_asset;
CustomAssetLibrary::AssetID next_material_asset;
std::vector<MaterialProperty2> vertex_properties;
std::vector<MaterialProperty2> pixel_properties;
std::optional<CullMode> cull_mode;
std::optional<DepthState> depth_state;
std::optional<BlendingState> blending_state;
std::vector<TextureSamplerValue> pixel_textures;
std::vector<CustomAssetLibrary::AssetID> render_targets;
};
class RasterMaterialAsset final : public CustomLoadableAsset<RasterMaterialData>
{
public:
using CustomLoadableAsset::CustomLoadableAsset;
private:
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
};
} // namespace VideoCommon

View file

@ -0,0 +1,200 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/Assets/MaterialAssetUtils.h"
#include "Common/VariantUtil.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/ShaderAsset.h"
namespace VideoCommon
{
void SetMaterialPropertiesFromShader(const VideoCommon::PixelShaderData& shader,
VideoCommon::MaterialData* material)
{
material->properties.clear();
for (const auto& entry : shader.m_properties)
{
// C++20: error with capturing structured bindings for our version of clang
const auto& name = entry.first;
const auto& shader_property = entry.second;
std::visit(overloaded{[&](const VideoCommon::ShaderProperty::Sampler2D& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value.value;
material->properties.push_back(std::move(property));
},
[&](const VideoCommon::ShaderProperty::Sampler2DArray& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value.value;
material->properties.push_back(std::move(property));
},
[&](const VideoCommon::ShaderProperty::SamplerCube& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value.value;
material->properties.push_back(std::move(property));
},
[&](s32 default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](const std::array<s32, 2>& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](const std::array<s32, 3>& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](const std::array<s32, 4>& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](float default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](const std::array<float, 2>& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](const std::array<float, 3>& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](const std::array<float, 4>& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
},
[&](const VideoCommon::ShaderProperty::RGB& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value.value;
material->properties.push_back(std::move(property));
},
[&](const VideoCommon::ShaderProperty::RGBA& default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value.value;
material->properties.push_back(std::move(property));
},
[&](bool default_value) {
VideoCommon::MaterialProperty property;
property.m_code_name = name;
property.m_value = default_value;
material->properties.push_back(std::move(property));
}},
shader_property.m_default);
}
}
void SetMaterialPropertiesFromShader(const RasterShaderData& shader, RasterMaterialData* material)
{
material->vertex_properties.clear();
material->pixel_properties.clear();
const auto set_properties = [&](const std::map<std::string, ShaderProperty2>& shader_properties,
std::vector<MaterialProperty2>* material_properties) {
for (const auto& entry : shader_properties)
{
// C++20: error with capturing structured bindings for our version of clang
// const auto& name = entry.first;
const auto& shader_property = entry.second;
std::visit(overloaded{[&](s32 default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](const std::array<s32, 2>& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](const std::array<s32, 3>& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](const std::array<s32, 4>& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](float default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](const std::array<float, 2>& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](const std::array<float, 3>& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](const std::array<float, 4>& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
},
[&](const VideoCommon::ShaderProperty2::RGB& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value.value;
material_properties->push_back(std::move(property));
},
[&](const VideoCommon::ShaderProperty2::RGBA& default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value.value;
material_properties->push_back(std::move(property));
},
[&](bool default_value) {
VideoCommon::MaterialProperty2 property;
property.m_value = default_value;
material_properties->push_back(std::move(property));
}},
shader_property.m_default);
}
};
set_properties(shader.m_vertex_properties, &material->vertex_properties);
set_properties(shader.m_pixel_properties, &material->pixel_properties);
}
void SetMaterialTexturesFromShader(const RasterShaderData& shader, RasterMaterialData* material)
{
material->pixel_textures.clear();
const auto set_textures = [&](const std::vector<RasterShaderData::SamplerData>& shader_samplers,
std::vector<TextureSamplerValue>* material_textures) {
for (std::size_t i = 0; i < shader_samplers.size(); i++)
{
TextureSamplerValue sampler_value;
material_textures->push_back(std::move(sampler_value));
}
};
set_textures(shader.m_pixel_samplers, &material->pixel_textures);
}
} // namespace VideoCommon

View file

@ -0,0 +1,19 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <string_view>
struct MaterialData;
struct PixelShaderData;
struct RasterMaterialData;
struct RasterShaderData;
namespace VideoCommon
{
void SetMaterialPropertiesFromShader(const PixelShaderData& shader, MaterialData* material);
void SetMaterialPropertiesFromShader(const RasterShaderData& shader, RasterMaterialData* material);
void SetMaterialTexturesFromShader(const RasterShaderData& shader, RasterMaterialData* material);
} // namespace VideoCommon

View file

@ -0,0 +1,43 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/Assets/RenderTargetAsset.h"
#include "Common/EnumUtils.h"
#include "Common/JsonUtil.h"
#include "Common/Logging/Log.h"
namespace VideoCommon
{
bool RenderTargetData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
const picojson::object& json, RenderTargetData* data)
{
data->m_texture_format = AbstractTextureFormat::RGBA8;
// TODO: parse format
return true;
}
void RenderTargetData::ToJson(picojson::object* obj, const RenderTargetData& data)
{
if (!obj) [[unlikely]]
return;
auto& json_obj = *obj;
json_obj.emplace("format", static_cast<double>(data.m_texture_format));
}
CustomAssetLibrary::LoadInfo
RenderTargetAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
{
auto potential_data = std::make_shared<RenderTargetData>();
const auto loaded_info = m_owning_library->LoadRenderTarget(asset_id, potential_data.get());
if (loaded_info.m_bytes_loaded == 0)
return {};
{
std::lock_guard lk(m_data_lock);
m_loaded = true;
m_data = std::move(potential_data);
}
return loaded_info;
}
} // namespace VideoCommon

View file

@ -0,0 +1,31 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <picojson.h>
#include "VideoCommon/Assets/CustomAsset.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/TextureConfig.h"
namespace VideoCommon
{
struct RenderTargetData
{
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
RenderTargetData* data);
static void ToJson(picojson::object* obj, const RenderTargetData& data);
AbstractTextureFormat m_texture_format;
SamplerState m_sampler;
};
class RenderTargetAsset final : public CustomLoadableAsset<RenderTargetData>
{
public:
using CustomLoadableAsset::CustomLoadableAsset;
private:
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
};
} // namespace VideoCommon

View file

@ -11,6 +11,8 @@
#include "Common/StringUtil.h"
#include "Common/VariantUtil.h"
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/Assets/TextureSamplerValue.h"
#include "VideoCommon/ShaderGenCommon.h"
namespace VideoCommon
{
@ -139,8 +141,17 @@ static bool ParseShaderValue(const CustomAssetLibrary::AssetID& asset_id,
if (json_value.is<std::string>())
{
ShaderProperty::Sampler2D sampler2d;
sampler2d.value = json_value.get<std::string>();
*value = std::move(sampler2d);
sampler2d.value.asset = json_value.get<std::string>();
*value = sampler2d;
return true;
}
else if (json_value.is<picojson::object>())
{
ShaderProperty::Sampler2D sampler2d;
auto texture_asset_obj = json_value.get<picojson::object>();
if (!TextureSamplerValue::FromJson(texture_asset_obj, &sampler2d.value))
return false;
*value = sampler2d;
return true;
}
}
@ -149,8 +160,17 @@ static bool ParseShaderValue(const CustomAssetLibrary::AssetID& asset_id,
if (json_value.is<std::string>())
{
ShaderProperty::Sampler2DArray sampler2darray;
sampler2darray.value = json_value.get<std::string>();
*value = std::move(sampler2darray);
sampler2darray.value.asset = json_value.get<std::string>();
*value = sampler2darray;
return true;
}
else if (json_value.is<picojson::object>())
{
ShaderProperty::Sampler2DArray sampler2darray;
auto texture_asset_obj = json_value.get<picojson::object>();
if (!TextureSamplerValue::FromJson(texture_asset_obj, &sampler2darray.value))
return false;
*value = sampler2darray;
return true;
}
}
@ -159,8 +179,83 @@ static bool ParseShaderValue(const CustomAssetLibrary::AssetID& asset_id,
if (json_value.is<std::string>())
{
ShaderProperty::SamplerCube samplercube;
samplercube.value = json_value.get<std::string>();
*value = std::move(samplercube);
samplercube.value.asset = json_value.get<std::string>();
*value = samplercube;
return true;
}
else if (json_value.is<picojson::object>())
{
ShaderProperty::SamplerCube samplercube;
auto texture_asset_obj = json_value.get<picojson::object>();
if (!TextureSamplerValue::FromJson(texture_asset_obj, &samplercube.value))
return false;
*value = samplercube;
return true;
}
}
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse the json, value is not valid for type '{}'",
asset_id, type);
return false;
}
static bool ParseShaderValue(const CustomAssetLibrary::AssetID& asset_id,
const picojson::value& json_value, std::string_view code_name,
std::string_view type, ShaderProperty2::Value* value)
{
if (type == "int")
{
return ParseNumeric<s32, 1>(asset_id, json_value, code_name, value);
}
else if (type == "int2")
{
return ParseNumeric<s32, 2>(asset_id, json_value, code_name, value);
}
else if (type == "int3")
{
return ParseNumeric<s32, 3>(asset_id, json_value, code_name, value);
}
else if (type == "int4")
{
return ParseNumeric<s32, 4>(asset_id, json_value, code_name, value);
}
else if (type == "float")
{
return ParseNumeric<float, 1>(asset_id, json_value, code_name, value);
}
else if (type == "float2")
{
return ParseNumeric<float, 2>(asset_id, json_value, code_name, value);
}
else if (type == "float3")
{
return ParseNumeric<float, 3>(asset_id, json_value, code_name, value);
}
else if (type == "float4")
{
return ParseNumeric<float, 4>(asset_id, json_value, code_name, value);
}
else if (type == "rgb")
{
ShaderProperty2::RGB rgb;
if (!ParseNumeric<float, 3>(asset_id, json_value, code_name, &rgb.value))
return false;
*value = std::move(rgb);
return true;
}
else if (type == "rgba")
{
ShaderProperty2::RGBA rgba;
if (!ParseNumeric<float, 4>(asset_id, json_value, code_name, &rgba.value))
return false;
*value = std::move(rgba);
return true;
}
else if (type == "bool")
{
if (json_value.is<bool>())
{
*value = json_value.get<bool>();
return true;
}
}
@ -250,6 +345,10 @@ ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
return false;
}
}
else
{
property.m_default = ShaderProperty::GetDefaultValueFromTypeName(type);
}
shader_properties->try_emplace(std::move(code_name), std::move(property));
}
@ -257,6 +356,260 @@ ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
return true;
}
static bool
ParseShaderProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
const picojson::array& properties_data,
std::map<std::string, VideoCommon::ShaderProperty2>* shader_properties)
{
if (!shader_properties) [[unlikely]]
return false;
for (const auto& property_data : properties_data)
{
VideoCommon::ShaderProperty2 property;
if (!property_data.is<picojson::object>())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, property is not the right json type",
asset_id);
return false;
}
const auto& property_data_obj = property_data.get<picojson::object>();
const auto type_iter = property_data_obj.find("type");
if (type_iter == property_data_obj.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, property entry 'type' not found",
asset_id);
return false;
}
if (!type_iter->second.is<std::string>())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse json, property entry 'type' is not "
"the right json type",
asset_id);
return false;
}
std::string type = type_iter->second.to_str();
Common::ToLower(&type);
const auto description_iter = property_data_obj.find("description");
if (description_iter == property_data_obj.end())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse json, property entry 'description' not found",
asset_id);
return false;
}
if (!description_iter->second.is<std::string>())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse json, property entry 'description' is not "
"the right json type",
asset_id);
return false;
}
property.m_description = description_iter->second.to_str();
const auto code_name_iter = property_data_obj.find("code_name");
if (code_name_iter == property_data_obj.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, property entry 'code_name' not found",
asset_id);
return false;
}
if (!code_name_iter->second.is<std::string>())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse json, property entry 'code_name' is not "
"the right json type",
asset_id);
return false;
}
std::string code_name = code_name_iter->second.to_str();
const auto default_iter = property_data_obj.find("default");
if (default_iter != property_data_obj.end())
{
if (!ParseShaderValue(asset_id, default_iter->second, code_name, type, &property.m_default))
{
return false;
}
}
else
{
property.m_default = ShaderProperty2::GetDefaultValueFromTypeName(type);
}
shader_properties->try_emplace(std::move(code_name), std::move(property));
}
return true;
}
bool RasterShaderData::FromJson(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
const picojson::object& json, RasterShaderData* data)
{
const auto parse_properties = [&](const char* name, std::string_view source,
std::map<std::string, ShaderProperty2>* properties) -> bool {
const auto properties_iter = json.find(name);
if (properties_iter == json.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' not found", asset_id, name);
return false;
}
if (!properties_iter->second.is<picojson::array>())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' is not the right json type",
asset_id, name);
return false;
}
const auto& properties_array = properties_iter->second.get<picojson::array>();
if (!ParseShaderProperties(asset_id, properties_array, properties))
return false;
for (const auto& [code_name, property] : *properties)
{
if (source.find(code_name) == std::string::npos)
{
ERROR_LOG_FMT(
VIDEO,
"Asset '{}' failed to parse json, the code name '{}' defined in the metadata was not "
"found in the source for '{}'",
asset_id, code_name, name);
return false;
}
}
return true;
};
const auto parse_samplers =
[&](const char* name,
std::vector<VideoCommon::RasterShaderData::SamplerData>* samplers) -> bool {
const auto samplers_iter = json.find(name);
if (samplers_iter == json.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' not found", asset_id, name);
return false;
}
if (!samplers_iter->second.is<picojson::array>())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' is not the right json type",
asset_id, name);
return false;
}
const auto& samplers_array = samplers_iter->second.get<picojson::array>();
if (!std::ranges::all_of(samplers_array, [](const picojson::value& json_data) {
return json_data.is<picojson::object>();
}))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' must contain objects", asset_id,
name);
return false;
}
for (const auto& sampler_json : samplers_array)
{
auto& sampler_json_obj = sampler_json.get<picojson::object>();
SamplerData sampler;
if (const auto sampler_name = ReadStringFromJson(sampler_json_obj, "name"))
{
sampler.name = *sampler_name;
}
else
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse sampler json, 'name' not found or wrong type",
asset_id);
return false;
}
if (const auto sampler_type =
ReadNumericFromJson<AbstractTextureType>(sampler_json_obj, "type"))
{
sampler.type = *sampler_type;
}
else
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' failed to parse sampler json, 'type' not found or wrong type",
asset_id);
return false;
}
samplers->push_back(std::move(sampler));
}
return true;
};
const auto parse_output_targets =
[&](const char* name,
std::vector<VideoCommon::RasterShaderData::OutputTargetData>* output_targets) -> bool {
const auto output_targets_iter = json.find(name);
if (output_targets_iter == json.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' not found", asset_id, name);
return false;
}
if (!output_targets_iter->second.is<picojson::array>())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' is not the right json type",
asset_id, name);
return false;
}
const auto& output_targets_array = output_targets_iter->second.get<picojson::array>();
if (!std::ranges::all_of(output_targets_array, [](const picojson::value& json_data) {
return json_data.is<picojson::object>();
}))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' failed to parse json, '{}' must contain objects", asset_id,
name);
return false;
}
for (const auto& output_target_json : output_targets_array)
{
auto& output_target_json_obj = output_target_json.get<picojson::object>();
OutputTargetData output_target;
if (const auto output_target_name = ReadStringFromJson(output_target_json_obj, "name"))
{
output_target.name = *output_target_name;
}
else
{
ERROR_LOG_FMT(
VIDEO, "Asset '{}' failed to parse output target json, 'name' not found or wrong type",
asset_id);
return false;
}
output_targets->push_back(std::move(output_target));
}
return true;
};
if (!parse_properties("vertex_properties", data->m_vertex_source, &data->m_vertex_properties))
return false;
if (!parse_properties("pixel_properties", data->m_pixel_source, &data->m_pixel_properties))
return false;
if (!parse_samplers("pixel_samplers", &data->m_pixel_samplers))
return false;
if (!parse_output_targets("pixel_output_targets", &data->m_output_targets))
return false;
return true;
}
bool PixelShaderData::FromJson(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
const picojson::object& json, PixelShaderData* data)
{
@ -303,15 +656,21 @@ void PixelShaderData::ToJson(picojson::object& obj, const PixelShaderData& data)
std::visit(overloaded{[&](const ShaderProperty::Sampler2D& default_value) {
json_property.emplace("type", "sampler2d");
json_property.emplace("default", default_value.value);
picojson::object texture_sampler_obj;
TextureSamplerValue::ToJson(&texture_sampler_obj, default_value.value);
json_property.emplace("default", picojson::value{texture_sampler_obj});
},
[&](const ShaderProperty::Sampler2DArray& default_value) {
json_property.emplace("type", "sampler2darray");
json_property.emplace("default", default_value.value);
picojson::object texture_sampler_obj;
TextureSamplerValue::ToJson(&texture_sampler_obj, default_value.value);
json_property.emplace("default", picojson::value{texture_sampler_obj});
},
[&](const ShaderProperty::SamplerCube& default_value) {
json_property.emplace("type", "samplercube");
json_property.emplace("default", default_value.value);
picojson::object texture_sampler_obj;
TextureSamplerValue::ToJson(&texture_sampler_obj, default_value.value);
json_property.emplace("default", picojson::value{texture_sampler_obj});
},
[&](s32 default_value) {
json_property.emplace("type", "int");
@ -365,6 +724,107 @@ void PixelShaderData::ToJson(picojson::object& obj, const PixelShaderData& data)
obj.emplace("properties", std::move(json_properties));
}
void RasterShaderData::ToJson(picojson::object& obj, const RasterShaderData& data)
{
const auto add_property = [](picojson::array* json_properties, const std::string& name,
const ShaderProperty2& property) {
picojson::object json_property;
json_property.emplace("code_name", name);
json_property.emplace("description", property.m_description);
std::visit(overloaded{[&](s32 default_value) {
json_property.emplace("type", "int");
json_property.emplace("default", static_cast<double>(default_value));
},
[&](const std::array<s32, 2>& default_value) {
json_property.emplace("type", "int2");
json_property.emplace("default", ToJsonArray(default_value));
},
[&](const std::array<s32, 3>& default_value) {
json_property.emplace("type", "int3");
json_property.emplace("default", ToJsonArray(default_value));
},
[&](const std::array<s32, 4>& default_value) {
json_property.emplace("type", "int4");
json_property.emplace("default", ToJsonArray(default_value));
},
[&](float default_value) {
json_property.emplace("type", "float");
json_property.emplace("default", static_cast<double>(default_value));
},
[&](const std::array<float, 2>& default_value) {
json_property.emplace("type", "float2");
json_property.emplace("default", ToJsonArray(default_value));
},
[&](const std::array<float, 3>& default_value) {
json_property.emplace("type", "float3");
json_property.emplace("default", ToJsonArray(default_value));
},
[&](const std::array<float, 4>& default_value) {
json_property.emplace("type", "float4");
json_property.emplace("default", ToJsonArray(default_value));
},
[&](const ShaderProperty2::RGB& default_value) {
json_property.emplace("type", "rgb");
json_property.emplace("default", ToJsonArray(default_value.value));
},
[&](const ShaderProperty2::RGBA& default_value) {
json_property.emplace("type", "rgba");
json_property.emplace("default", ToJsonArray(default_value.value));
},
[&](bool default_value) {
json_property.emplace("type", "bool");
json_property.emplace("default", default_value);
}},
property.m_default);
json_properties->emplace_back(std::move(json_property));
};
const auto add_sampler = [](picojson::array* json_samplers, const SamplerData& sampler) {
picojson::object json_sampler;
json_sampler.emplace("name", sampler.name);
json_sampler.emplace("type", static_cast<double>(sampler.type));
json_samplers->emplace_back(std::move(json_sampler));
};
const auto add_output_target = [](picojson::array* json_output_targets,
const OutputTargetData& output_target) {
picojson::object json_output_target;
json_output_target.emplace("name", output_target.name);
json_output_targets->emplace_back(std::move(json_output_target));
};
picojson::array json_vertex_properties;
for (const auto& [name, property] : data.m_vertex_properties)
{
add_property(&json_vertex_properties, name, property);
}
obj.emplace("vertex_properties", std::move(json_vertex_properties));
picojson::array json_pixel_properties;
for (const auto& [name, property] : data.m_pixel_properties)
{
add_property(&json_pixel_properties, name, property);
}
obj.emplace("pixel_properties", std::move(json_pixel_properties));
picojson::array json_pixel_samplers;
for (const auto& sampler : data.m_pixel_samplers)
{
add_sampler(&json_pixel_samplers, sampler);
}
obj.emplace("pixel_samplers", json_pixel_samplers);
picojson::array json_output_targets;
for (const auto& output_target : data.m_output_targets)
{
add_output_target(&json_output_targets, output_target);
}
obj.emplace("pixel_output_targets", json_output_targets);
}
std::span<const std::string_view> ShaderProperty::GetValueTypeNames()
{
static constexpr std::array<std::string_view, 14> values = {
@ -373,6 +833,13 @@ std::span<const std::string_view> ShaderProperty::GetValueTypeNames()
return values;
}
std::span<const std::string_view> ShaderProperty2::GetValueTypeNames()
{
static constexpr std::array<std::string_view, 11> values = {
"int", "int2", "int3", "int4", "float", "float2", "float3", "float4", "rgb", "rgba", "bool"};
return values;
}
ShaderProperty::Value ShaderProperty::GetDefaultValueFromTypeName(std::string_view name)
{
if (name == "sampler2d")
@ -435,6 +902,88 @@ ShaderProperty::Value ShaderProperty::GetDefaultValueFromTypeName(std::string_vi
return Value{};
}
ShaderProperty2::Value ShaderProperty2::GetDefaultValueFromTypeName(std::string_view name)
{
if (name == "int")
{
return 0;
}
else if (name == "int2")
{
return std::array<s32, 2>{};
}
else if (name == "int3")
{
return std::array<s32, 3>{};
}
else if (name == "int4")
{
return std::array<s32, 4>{};
}
else if (name == "float")
{
return 0.0f;
}
else if (name == "float2")
{
return std::array<float, 2>{};
}
else if (name == "float3")
{
return std::array<float, 3>{};
}
else if (name == "float4")
{
return std::array<float, 4>{};
}
else if (name == "rgb")
{
return RGB{};
}
else if (name == "rgba")
{
return RGBA{};
}
else if (name == "bool")
{
return false;
}
return Value{};
}
void ShaderProperty2::WriteAsShaderCode(ShaderCode& shader_source, std::string_view name,
const ShaderProperty2& property)
{
const auto write_shader = [&](std::string_view type, u32 element_count) {
if (element_count == 1)
{
shader_source.Write("{} {};\n", type, name);
}
else
{
shader_source.Write("{}{} {};\n", type, element_count, name);
}
for (std::size_t i = element_count; i < 4; i++)
{
shader_source.Write("{} {}_padding_{};\n", type, name, i + 1);
}
};
std::visit(overloaded{[&](s32) { write_shader("int", 1); },
[&](const std::array<s32, 2>&) { write_shader("int", 2); },
[&](const std::array<s32, 3>&) { write_shader("int", 3); },
[&](const std::array<s32, 4>&) { write_shader("int", 4); },
[&](float) { write_shader("float", 1); },
[&](const std::array<float, 2>&) { write_shader("float", 2); },
[&](const std::array<float, 3>&) { write_shader("float", 3); },
[&](const std::array<float, 4>&) { write_shader("float", 4); },
[&](const ShaderProperty2::RGB&) { write_shader("float", 3); },
[&](const ShaderProperty2::RGBA&) { write_shader("float", 4); },
[&](bool) { write_shader("bool", 1); }},
property.m_default);
}
CustomAssetLibrary::LoadInfo PixelShaderAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
{
auto potential_data = std::make_shared<PixelShaderData>();
@ -448,4 +997,19 @@ CustomAssetLibrary::LoadInfo PixelShaderAsset::LoadImpl(const CustomAssetLibrary
}
return loaded_info;
}
CustomAssetLibrary::LoadInfo
RasterShaderAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
{
auto potential_data = std::make_shared<RasterShaderData>();
const auto loaded_info = m_owning_library->LoadShader(asset_id, potential_data.get());
if (loaded_info.m_bytes_loaded == 0)
return {};
{
std::lock_guard lk(m_data_lock);
m_loaded = true;
m_data = std::move(potential_data);
}
return loaded_info;
}
} // namespace VideoCommon

View file

@ -13,6 +13,10 @@
#include <picojson.h>
#include "VideoCommon/Assets/CustomAsset.h"
#include "VideoCommon/Assets/TextureSamplerValue.h"
#include "VideoCommon/TextureConfig.h"
class ShaderCode;
namespace VideoCommon
{
@ -30,17 +34,17 @@ struct ShaderProperty
struct Sampler2D
{
CustomAssetLibrary::AssetID value;
TextureSamplerValue value;
};
struct Sampler2DArray
{
CustomAssetLibrary::AssetID value;
TextureSamplerValue value;
};
struct SamplerCube
{
CustomAssetLibrary::AssetID value;
TextureSamplerValue value;
};
using Value = std::variant<s32, std::array<s32, 2>, std::array<s32, 3>, std::array<s32, 4>, float,
@ -52,6 +56,31 @@ struct ShaderProperty
Value m_default;
std::string m_description;
};
struct ShaderProperty2
{
struct RGB
{
std::array<float, 3> value;
};
struct RGBA
{
std::array<float, 4> value;
};
using Value = std::variant<s32, std::array<s32, 2>, std::array<s32, 3>, std::array<s32, 4>, float,
std::array<float, 2>, std::array<float, 3>, std::array<float, 4>, bool,
RGB, RGBA>;
static std::span<const std::string_view> GetValueTypeNames();
static Value GetDefaultValueFromTypeName(std::string_view name);
static void WriteAsShaderCode(ShaderCode& shader_source, std::string_view name,
const ShaderProperty2& property);
Value m_default;
std::string m_description;
};
struct PixelShaderData
{
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
@ -71,6 +100,49 @@ class PixelShaderAsset final : public CustomLoadableAsset<PixelShaderData>
public:
using CustomLoadableAsset::CustomLoadableAsset;
private:
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
};
struct RasterShaderData
{
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
RasterShaderData* data);
static void ToJson(picojson::object& obj, const RasterShaderData& data);
// These shader properties describe the input that the
// shader expects to expose. The key is text
// expected to be in the shader code and the propery
// describes various details about the input
std::map<std::string, ShaderProperty2> m_vertex_properties;
std::string m_vertex_source;
std::map<std::string, ShaderProperty2> m_pixel_properties;
std::string m_pixel_source;
struct SamplerData
{
AbstractTextureType type;
std::string name;
bool operator==(const SamplerData&) const = default;
};
std::vector<SamplerData> m_pixel_samplers;
struct OutputTargetData
{
std::string name;
bool operator==(const OutputTargetData&) const = default;
};
std::vector<OutputTargetData> m_output_targets;
};
class RasterShaderAsset final : public CustomLoadableAsset<RasterShaderData>
{
public:
using CustomLoadableAsset::CustomLoadableAsset;
private:
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
};

View file

@ -0,0 +1,77 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/Assets/TextureSamplerValue.h"
#include <optional>
#include "Common/EnumUtils.h"
#include "Common/JsonUtil.h"
#include "Common/StringUtil.h"
namespace VideoCommon
{
namespace
{
std::optional<TextureSamplerValue::SamplerOrigin>
ReadSamplerOriginFromJSON(const picojson::object& json)
{
auto sampler_origin = ReadStringFromJson(json, "sampler_origin").value_or("");
Common::ToLower(&sampler_origin);
if (sampler_origin == "asset")
{
return TextureSamplerValue::SamplerOrigin::Asset;
}
else if (sampler_origin == "texture_hash")
{
return TextureSamplerValue::SamplerOrigin::TextureHash;
}
return std::nullopt;
}
} // namespace
std::string TextureSamplerValue::ToString(SamplerOrigin sampler_origin)
{
if (sampler_origin == SamplerOrigin::Asset)
return "asset";
return "texture_hash";
}
bool TextureSamplerValue::FromJson(const picojson::object& json, TextureSamplerValue* data)
{
data->asset = ReadStringFromJson(json, "asset").value_or("");
data->texture_hash = ReadStringFromJson(json, "texture_hash").value_or("");
data->sampler_origin =
ReadSamplerOriginFromJSON(json).value_or(TextureSamplerValue::SamplerOrigin::Asset);
// Render targets
data->is_render_target = ReadBoolFromJson(json, "is_render_target").value_or(false);
data->camera_originating_draw_call =
GraphicsModSystem::DrawCallID{ReadNumericFromJson<u64>(json, "camera_draw_call").value_or(0)};
data->camera_type = static_cast<GraphicsModSystem::CameraType>(
ReadNumericFromJson<u8>(json, "camera_type").value_or(0));
return true;
}
void TextureSamplerValue::ToJson(picojson::object* obj, const TextureSamplerValue& data)
{
if (!obj) [[unlikely]]
return;
obj->emplace("asset", data.asset);
obj->emplace("texture_hash", data.texture_hash);
obj->emplace("sampler_origin", ToString(data.sampler_origin));
obj->emplace("is_render_target", data.is_render_target);
const auto camera_draw_call = static_cast<double>(
Common::ToUnderlying<GraphicsModSystem::DrawCallID>(data.camera_originating_draw_call));
obj->emplace("camera_draw_call", camera_draw_call);
const auto camera_type =
static_cast<double>(Common::ToUnderlying<GraphicsModSystem::CameraType>(data.camera_type));
obj->emplace("camera_type", camera_type);
}
} // namespace VideoCommon

View file

@ -0,0 +1,35 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <picojson.h>
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/GraphicsModSystem/Types.h"
namespace VideoCommon
{
struct TextureSamplerValue
{
CustomAssetLibrary::AssetID asset;
enum class SamplerOrigin
{
Asset,
TextureHash
};
static std::string ToString(SamplerOrigin sampler_origin);
SamplerOrigin sampler_origin = SamplerOrigin::Asset;
std::string texture_hash;
bool is_render_target = false;
GraphicsModSystem::CameraType camera_type;
GraphicsModSystem::DrawCallID camera_originating_draw_call;
static bool FromJson(const picojson::object& json, TextureSamplerValue* data);
static void ToJson(picojson::object* obj, const TextureSamplerValue& data);
};
} // namespace VideoCommon

View file

@ -0,0 +1,13 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include <map>
#include <string>
namespace VideoCommon::Assets
{
using AssetMap = std::map<std::string, std::filesystem::path>;
}

View file

@ -0,0 +1,57 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/Assets/WatchableFilesystemAssetLibrary.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
namespace VideoCommon
{
void WatchableFilesystemAssetLibrary::Watch(const std::string& path)
{
const auto [iter, inserted] = m_watched_paths.try_emplace(path, nullptr);
if (inserted)
{
iter->second = std::make_unique<wtr::watch>(path, [this](wtr::event e) {
if (e.path_type == wtr::event::path_type::watcher)
{
return;
}
if (e.effect_type == wtr::event::effect_type::create)
{
const auto path = WithUnifiedPathSeparators(PathToString(e.path_name));
PathAdded(path);
}
else if (e.effect_type == wtr::event::effect_type::modify)
{
const auto path = WithUnifiedPathSeparators(PathToString(e.path_name));
PathModified(path);
}
else if (e.effect_type == wtr::event::effect_type::rename)
{
if (!e.associated)
{
WARN_LOG_FMT(VIDEO, "Rename on path seen without association!");
return;
}
const auto old_path = WithUnifiedPathSeparators(PathToString(e.path_name));
const auto new_path = WithUnifiedPathSeparators(PathToString(e.associated->path_name));
PathRenamed(old_path, new_path);
}
else if (e.effect_type == wtr::event::effect_type::destroy)
{
const auto path = WithUnifiedPathSeparators(PathToString(e.path_name));
PathDeleted(path);
}
});
}
}
void WatchableFilesystemAssetLibrary::Unwatch(const std::string& path)
{
m_watched_paths.erase(path);
}
} // namespace VideoCommon

View file

@ -0,0 +1,38 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <wtr/watcher.hpp>
#include "VideoCommon/Assets/CustomAssetLibrary.h"
namespace VideoCommon
{
class WatchableFilesystemAssetLibrary : public CustomAssetLibrary
{
public:
void Watch(const std::string& path);
void Unwatch(const std::string& path);
private:
// A new file or folder was added to one of the watched paths
virtual void PathAdded(std::string_view path) {}
// A file or folder was modified in one of the watched paths
virtual void PathModified(std::string_view path) {}
// A file or folder was renamed in one of the watched paths
virtual void PathRenamed(std::string_view old_path, std::string_view new_path) {}
// A file or folder was deleted in one of the watched paths
virtual void PathDeleted(std::string_view path) {}
std::map<std::string, std::unique_ptr<wtr::watch>> m_watched_paths;
};
} // namespace VideoCommon

View file

@ -14,18 +14,29 @@ add_library(videocommon
Assets/CustomAssetLibrary.h
Assets/CustomAssetLoader.cpp
Assets/CustomAssetLoader.h
Assets/CustomAssetLoader2.cpp
Assets/CustomAssetLoader2.h
Assets/CustomTextureData.cpp
Assets/CustomTextureData.h
Assets/DirectFilesystemAssetLibrary.cpp
Assets/DirectFilesystemAssetLibrary.h
Assets/MaterialAsset.cpp
Assets/MaterialAsset.h
Assets/MaterialAssetUtils.cpp
Assets/MaterialAssetUtils.h
Assets/MeshAsset.cpp
Assets/MeshAsset.h
Assets/RenderTargetAsset.cpp
Assets/RenderTargetAsset.h
Assets/ShaderAsset.cpp
Assets/ShaderAsset.h
Assets/TextureAsset.cpp
Assets/TextureAsset.h
Assets/TextureSamplerValue.cpp
Assets/TextureSamplerValue.h
Assets/Types.h
Assets/WatchableFilesystemAssetLibrary.cpp
Assets/WatchableFilesystemAssetLibrary.h
AsyncRequests.cpp
AsyncRequests.h
AsyncShaderCompiler.cpp
@ -66,41 +77,113 @@ add_library(videocommon
GeometryShaderGen.h
GeometryShaderManager.cpp
GeometryShaderManager.h
GraphicsModEditor/Controls/AssetDisplay.cpp
GraphicsModEditor/Controls/AssetDisplay.h
GraphicsModEditor/Controls/CameraChoiceControl.cpp
GraphicsModEditor/Controls/CameraChoiceControl.h
GraphicsModEditor/Controls/MaterialControl.cpp
GraphicsModEditor/Controls/MaterialControl.h
GraphicsModEditor/Controls/MaterialGenerateWindow.cpp
GraphicsModEditor/Controls/MaterialGenerateWindow.h
GraphicsModEditor/Controls/MeshControl.cpp
GraphicsModEditor/Controls/MeshControl.h
GraphicsModEditor/Controls/MeshExtractWindow.cpp
GraphicsModEditor/Controls/MeshExtractWindow.h
GraphicsModEditor/Controls/MeshImportWindow.cpp
GraphicsModEditor/Controls/MeshImportWindow.h
GraphicsModEditor/Controls/MiscControls.cpp
GraphicsModEditor/Controls/MiscControls.h
GraphicsModEditor/Controls/RenderTargetControl.cpp
GraphicsModEditor/Controls/RenderTargetControl.h
GraphicsModEditor/Controls/ShaderControl.cpp
GraphicsModEditor/Controls/ShaderControl.h
GraphicsModEditor/Controls/TagSelectionWindow.cpp
GraphicsModEditor/Controls/TagSelectionWindow.h
GraphicsModEditor/Controls/TextureControl.cpp
GraphicsModEditor/Controls/TextureControl.h
GraphicsModEditor/EditorAssetSource.cpp
GraphicsModEditor/EditorAssetSource.h
GraphicsModEditor/EditorBackend.cpp
GraphicsModEditor/EditorBackend.h
GraphicsModEditor/EditorEvents.h
GraphicsModEditor/EditorFilter.cpp
GraphicsModEditor/EditorFilter.h
GraphicsModEditor/EditorMain.cpp
GraphicsModEditor/EditorMain.h
GraphicsModEditor/EditorState.cpp
GraphicsModEditor/EditorState.h
GraphicsModEditor/EditorTypes.h
GraphicsModEditor/MaterialGeneration.cpp
GraphicsModEditor/MaterialGeneration.h
GraphicsModEditor/Panels/ActiveTargetsPanel.cpp
GraphicsModEditor/Panels/ActiveTargetsPanel.h
GraphicsModEditor/Panels/AssetBrowserPanel.cpp
GraphicsModEditor/Panels/AssetBrowserPanel.h
GraphicsModEditor/Panels/PropertiesPanel.cpp
GraphicsModEditor/Panels/PropertiesPanel.h
GraphicsModEditor/RenderTargetGeneration.cpp
GraphicsModEditor/RenderTargetGeneration.h
GraphicsModEditor/SceneDumper.cpp
GraphicsModEditor/SceneDumper.h
GraphicsModEditor/SceneUtils.cpp
GraphicsModEditor/SceneUtils.h
GraphicsModEditor/ShaderGeneration.cpp
GraphicsModEditor/ShaderGeneration.h
GraphicsModSystem/Config/GraphicsMod.cpp
GraphicsModSystem/Config/GraphicsMod.h
GraphicsModSystem/Config/GraphicsModAction.cpp
GraphicsModSystem/Config/GraphicsModAction.h
GraphicsModSystem/Config/GraphicsModAsset.cpp
GraphicsModSystem/Config/GraphicsModAsset.h
GraphicsModSystem/Config/GraphicsModFeature.cpp
GraphicsModSystem/Config/GraphicsModFeature.h
GraphicsModSystem/Config/GraphicsModGroup.cpp
GraphicsModSystem/Config/GraphicsModGroup.h
GraphicsModSystem/Config/GraphicsModHashPolicy.cpp
GraphicsModSystem/Config/GraphicsModHashPolicy.h
GraphicsModSystem/Config/GraphicsModTag.cpp
GraphicsModSystem/Config/GraphicsModTag.h
GraphicsModSystem/Config/GraphicsTarget.cpp
GraphicsModSystem/Config/GraphicsTarget.h
GraphicsModSystem/Config/GraphicsTargetGroup.cpp
GraphicsModSystem/Config/GraphicsTargetGroup.h
GraphicsModSystem/Constants.h
GraphicsModSystem/Runtime/Actions/CustomMeshAction.cpp
GraphicsModSystem/Runtime/Actions/CustomMeshAction.h
GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
GraphicsModSystem/Runtime/Actions/ModifyLight.cpp
GraphicsModSystem/Runtime/Actions/ModifyLight.h
GraphicsModSystem/Runtime/Actions/MoveAction.cpp
GraphicsModSystem/Runtime/Actions/MoveAction.h
GraphicsModSystem/Runtime/Actions/PrintAction.cpp
GraphicsModSystem/Runtime/Actions/PrintAction.h
GraphicsModSystem/Runtime/Actions/RelativeCameraAction.cpp
GraphicsModSystem/Runtime/Actions/RelativeCameraAction.h
GraphicsModSystem/Runtime/Actions/ScaleAction.cpp
GraphicsModSystem/Runtime/Actions/ScaleAction.h
GraphicsModSystem/Runtime/Actions/SkipAction.cpp
GraphicsModSystem/Runtime/Actions/SkipAction.h
GraphicsModSystem/Runtime/CustomPipeline.cpp
GraphicsModSystem/Runtime/CustomPipeline.h
GraphicsModSystem/Runtime/CustomShaderCache.cpp
GraphicsModSystem/Runtime/CustomShaderCache.h
GraphicsModSystem/Runtime/Actions/TransformAction.cpp
GraphicsModSystem/Runtime/Actions/TransformAction.h
GraphicsModSystem/Runtime/CameraManager.cpp
GraphicsModSystem/Runtime/CameraManager.h
GraphicsModSystem/Runtime/CustomResourceManager.cpp
GraphicsModSystem/Runtime/CustomResourceManager.h
GraphicsModSystem/Runtime/CustomShaderCache2.cpp
GraphicsModSystem/Runtime/CustomShaderCache2.h
GraphicsModSystem/Runtime/CustomTextureCache2.cpp
GraphicsModSystem/Runtime/CustomTextureCache2.h
GraphicsModSystem/Runtime/FBInfo.cpp
GraphicsModSystem/Runtime/FBInfo.h
GraphicsModSystem/Runtime/GraphicsModAction.h
GraphicsModSystem/Runtime/GraphicsModActionData.h
GraphicsModSystem/Runtime/GraphicsModActionFactory.cpp
GraphicsModSystem/Runtime/GraphicsModActionFactory.h
GraphicsModSystem/Runtime/GraphicsModBackend.cpp
GraphicsModSystem/Runtime/GraphicsModBackend.h
GraphicsModSystem/Runtime/GraphicsModHash.cpp
GraphicsModSystem/Runtime/GraphicsModHash.h
GraphicsModSystem/Runtime/GraphicsModManager.cpp
GraphicsModSystem/Runtime/GraphicsModManager.h
GraphicsModSystem/Runtime/GraphicsModRuntimeBackend.cpp
GraphicsModSystem/Runtime/GraphicsModRuntimeBackend.h
HiresTextures.cpp
HiresTextures.h
IndexGenerator.cpp
@ -219,6 +302,7 @@ PRIVATE
implot
glslang
tinygltf
watcher
)
if(_M_X86_64)

View file

@ -103,6 +103,8 @@ struct alignas(16) VertexShaderConstants
u32 vertex_offset_posmtx;
std::array<u32, 2> vertex_offset_colors;
std::array<u32, 8> vertex_offset_texcoords;
// For custom meshes (TODO: move)
std::array<float4, 4> custom_transform;
};
enum class VSExpand : u32

View file

@ -0,0 +1,267 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModEditor/Controls/AssetDisplay.h"
#include "VideoCommon/GraphicsModEditor/EditorEvents.h"
#include <algorithm>
#include <array>
#include <string_view>
#include <imgui.h>
namespace GraphicsModEditor::Controls
{
namespace
{
std::string_view AssetDragDropTypeFromType(AssetDataType asset_type)
{
switch (asset_type)
{
case Material:
return "MaterialAsset";
case RasterMaterial:
return "RasterMaterialAsset";
case PixelShader:
return "PixelShaderAsset";
case Shader:
return "ShaderAsset";
case Texture:
return "TextureAsset";
case Mesh:
return "MeshAsset";
}
return "";
}
AbstractTexture* GenericImageIconFromType(AssetDataType asset_type, const EditorState& state)
{
switch (asset_type)
{
case Material:
if (const auto it = state.m_editor_data.m_name_to_texture.find("file");
it != state.m_editor_data.m_name_to_texture.end())
{
return it->second.get();
}
case RasterMaterial:
if (const auto it = state.m_editor_data.m_name_to_texture.find("file");
it != state.m_editor_data.m_name_to_texture.end())
{
return it->second.get();
}
case PixelShader:
if (const auto it = state.m_editor_data.m_name_to_texture.find("code");
it != state.m_editor_data.m_name_to_texture.end())
{
return it->second.get();
}
case Shader:
if (const auto it = state.m_editor_data.m_name_to_texture.find("code");
it != state.m_editor_data.m_name_to_texture.end())
{
return it->second.get();
}
case Texture:
if (const auto it = state.m_editor_data.m_name_to_texture.find("image");
it != state.m_editor_data.m_name_to_texture.end())
{
return it->second.get();
}
case Mesh:
if (const auto it = state.m_editor_data.m_name_to_texture.find("file");
it != state.m_editor_data.m_name_to_texture.end())
{
return it->second.get();
}
}
return nullptr;
}
ImVec2 asset_button_size{150, 150};
} // namespace
bool AssetDisplay(std::string_view popup_name, EditorState* state,
VideoCommon::CustomAssetLibrary::AssetID* asset_id_chosen,
AssetDataType* asset_type_chosen,
std::span<const AssetDataType> asset_types_allowed)
{
if (!state) [[unlikely]]
return false;
if (!asset_id_chosen) [[unlikely]]
return false;
if (!asset_type_chosen) [[unlikely]]
return false;
static std::string asset_filter_text = "";
const std::string reset_popup_name = std::string{popup_name} + "Reset";
bool changed = false;
const EditorAsset* asset = nullptr;
if (!asset_id_chosen->empty())
{
asset = state->m_user_data.m_asset_library->GetAssetFromID(*asset_id_chosen);
}
if (!asset)
{
if (ImGui::Button("None", asset_button_size))
{
if (!ImGui::IsPopupOpen(popup_name.data()))
{
asset_filter_text = "";
ImGui::OpenPopup(popup_name.data());
}
}
}
else
{
AbstractTexture* texture =
state->m_user_data.m_asset_library->GetAssetPreview(asset->m_asset_id);
state->m_editor_data.m_assets_waiting_for_preview.erase(asset->m_asset_id);
ImGui::BeginGroup();
if (texture)
{
if (ImGui::ImageButton(asset->m_asset_id.c_str(), *texture, asset_button_size))
{
if (!ImGui::IsPopupOpen(popup_name.data()))
{
asset_filter_text = "";
ImGui::OpenPopup(popup_name.data());
}
}
if (ImGui::BeginPopupContextItem(reset_popup_name.data()))
{
if (ImGui::Selectable("View properties"))
{
EditorEvents::JumpToAssetInBrowserEvent::Trigger(asset->m_asset_id);
}
if (ImGui::Selectable("Reset"))
{
*asset_id_chosen = "";
changed = true;
}
ImGui::EndPopup();
}
ImGui::TextWrapped("%s", PathToString(asset->m_asset_path.stem()).c_str());
}
else
{
if (ImGui::Button(PathToString(asset->m_asset_path).c_str(), asset_button_size))
{
if (!ImGui::IsPopupOpen(popup_name.data()))
{
asset_filter_text = "";
ImGui::OpenPopup(popup_name.data());
}
}
}
ImGui::EndGroup();
}
if (ImGui::BeginDragDropTarget())
{
for (const auto asset_type_allowed : asset_types_allowed)
{
if (const ImGuiPayload* payload =
ImGui::AcceptDragDropPayload(AssetDragDropTypeFromType(asset_type_allowed).data()))
{
VideoCommon::CustomAssetLibrary::AssetID new_asset_id(
static_cast<const char*>(payload->Data), payload->DataSize);
*asset_id_chosen = new_asset_id;
*asset_type_chosen = asset_type_allowed;
changed = true;
}
}
ImGui::EndDragDropTarget();
}
// Asset browser popup below
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
const ImVec2 size = ImGui::GetMainViewport()->WorkSize;
ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(size.x / 4.0f, size.y / 2.0f));
if (ImGui::BeginPopup(popup_name.data()))
{
const u32 column_count = 5;
u32 current_columns = 0;
u32 assets_displayed = 0;
const float search_size = 200.0f;
ImGui::SetNextItemWidth(search_size);
ImGui::InputTextWithHint("##", "Search...", &asset_filter_text);
if (ImGui::BeginTable("AssetBrowserPopupTable", column_count))
{
ImGui::TableNextRow();
for (const auto& asset_from_library : state->m_user_data.m_asset_library->GetAllAssets())
{
if (std::ranges::find(asset_types_allowed, asset_from_library.m_data_type) ==
asset_types_allowed.end())
{
continue;
}
const auto name = PathToString(asset_from_library.m_asset_path.stem());
if (!asset_filter_text.empty() && name.find(asset_filter_text) == std::string::npos)
{
continue;
}
assets_displayed++;
ImGui::TableNextColumn();
ImGui::BeginGroup();
AbstractTexture* texture =
state->m_user_data.m_asset_library->GetAssetPreview(asset_from_library.m_asset_id);
if (texture)
{
if (ImGui::ImageButton(asset_from_library.m_asset_id.c_str(), *texture,
asset_button_size))
{
*asset_id_chosen = asset_from_library.m_asset_id;
changed = true;
ImGui::CloseCurrentPopup();
}
ImGui::TextWrapped("%s", name.c_str());
}
else
{
if (ImGui::Button(name.c_str(), asset_button_size))
{
*asset_id_chosen = asset_from_library.m_asset_id;
changed = true;
ImGui::CloseCurrentPopup();
}
}
ImGui::EndGroup();
current_columns++;
if (current_columns == column_count)
{
ImGui::TableNextRow();
current_columns = 0;
}
}
ImGui::EndTable();
}
if (assets_displayed == 0)
{
ImGui::Text("No assets found");
}
ImGui::EndPopup();
}
return changed;
}
bool AssetDisplay(std::string_view popup_name, EditorState* state,
VideoCommon::CustomAssetLibrary::AssetID* asset_id_chosen,
AssetDataType asset_type_allowed)
{
AssetDataType asset_data_type_chosen;
return AssetDisplay(popup_name, state, asset_id_chosen, &asset_data_type_chosen,
std::array<AssetDataType, 1>{asset_type_allowed});
}
} // namespace GraphicsModEditor::Controls

View file

@ -0,0 +1,24 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <string_view>
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/GraphicsModEditor/EditorState.h"
#include "VideoCommon/GraphicsModEditor/EditorTypes.h"
namespace GraphicsModEditor::Controls
{
// Displays an asset that can be overwritten by dragging/dropping or selecting from an asset browser
// Returns true if asset id changed
bool AssetDisplay(std::string_view popup_name, EditorState* state,
VideoCommon::CustomAssetLibrary::AssetID* asset_id_chosen,
AssetDataType* asset_type_chosen,
std::span<const AssetDataType> asset_types_allowed);
bool AssetDisplay(std::string_view popup_name, EditorState* state,
VideoCommon::CustomAssetLibrary::AssetID* asset_id_chosen,
AssetDataType asset_type_allowed);
} // namespace GraphicsModEditor::Controls

View file

@ -0,0 +1,117 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModEditor/Controls/CameraChoiceControl.h"
#include <chrono>
#include <imgui.h>
#include "Core/System.h"
#include "VideoCommon/GraphicsModEditor/EditorBackend.h"
#include "VideoCommon/GraphicsModEditor/SceneUtils.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/RelativeCameraAction.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
namespace GraphicsModEditor::Controls
{
namespace
{
ImVec2 camera_button_size{150, 150};
std::string GetCameraName(const EditorState& editor_state, GraphicsModSystem::DrawCallID draw_call)
{
std::string camera_name = "";
const auto actions = GetActionsForDrawCall<RelativeCameraAction>(editor_state, draw_call);
if (!actions.empty())
{
const std::string draw_call_name = GetDrawCallName(editor_state, draw_call);
camera_name = fmt::format("{}/{}", draw_call_name, GetActionName(actions[0]));
}
return camera_name;
}
} // namespace
bool CameraChoiceControl(std::string_view popup_name, EditorState* editor_state,
GraphicsModSystem::DrawCallID* draw_call_chosen)
{
if (!editor_state) [[unlikely]]
return false;
if (!draw_call_chosen) [[unlikely]]
return false;
const std::string camera_name = GetCameraName(*editor_state, *draw_call_chosen);
if (camera_name.empty())
ImGui::Text("Camera: None");
else
ImGui::Text("Camera: %s", camera_name.c_str());
if (ImGui::Button("Pick camera"))
{
ImGui::OpenPopup(popup_name.data());
}
bool changed = false;
// Camera popup below
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
const ImVec2 size = ImGui::GetMainViewport()->WorkSize;
ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(size.x / 4.0f, size.y / 2.0f));
if (ImGui::BeginPopup(popup_name.data()))
{
const u32 column_count = 5;
u32 current_columns = 0;
u32 cameras_displayed = 0;
const float search_size = 200.0f;
ImGui::SetNextItemWidth(search_size);
std::string camera_filter = "";
ImGui::InputTextWithHint("##", "Search...", &camera_filter);
if (ImGui::BeginTable("CameraPopupTable", column_count))
{
ImGui::TableNextRow();
auto& manager = Core::System::GetInstance().GetGraphicsModManager();
auto& backend = static_cast<GraphicsModEditor::EditorBackend&>(manager.GetBackend());
const auto camera_ids = backend.GetCameraManager().GetDrawCallsWithCameras();
for (const auto& draw_call : camera_ids)
{
const std::string camera_name_in_popup = GetCameraName(*editor_state, draw_call);
if (camera_name_in_popup.empty())
continue;
if (!camera_filter.empty() && camera_name_in_popup.find(camera_filter) == std::string::npos)
{
continue;
}
cameras_displayed++;
ImGui::TableNextColumn();
if (ImGui::Button(camera_name_in_popup.c_str()))
{
*draw_call_chosen = draw_call;
changed = true;
ImGui::CloseCurrentPopup();
}
current_columns++;
if (current_columns == column_count)
{
ImGui::TableNextRow();
current_columns = 0;
}
}
ImGui::EndTable();
}
if (cameras_displayed == 0)
{
ImGui::Text("No cameras found");
}
ImGui::EndPopup();
}
return changed;
}
} // namespace GraphicsModEditor::Controls

View file

@ -0,0 +1,15 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string_view>
#include "VideoCommon/GraphicsModEditor/EditorState.h"
#include "VideoCommon/GraphicsModSystem/Types.h"
namespace GraphicsModEditor::Controls
{
bool CameraChoiceControl(std::string_view popup_name, EditorState* editor_state,
GraphicsModSystem::DrawCallID* draw_call_chosen);
} // namespace GraphicsModEditor::Controls

View file

@ -0,0 +1,831 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModEditor/Controls/MaterialControl.h"
#include <string>
#include <variant>
#include <fmt/format.h>
#include <imgui.h>
#include <misc/cpp/imgui_stdlib.h>
#include "Common/EnumUtils.h"
#include "Common/VariantUtil.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/MaterialAssetUtils.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/GraphicsModEditor/Controls/AssetDisplay.h"
#include "VideoCommon/GraphicsModEditor/Controls/CameraChoiceControl.h"
#include "VideoCommon/GraphicsModEditor/EditorAssetSource.h"
#include "VideoCommon/GraphicsModEditor/EditorEvents.h"
#include "VideoCommon/RenderState.h"
namespace GraphicsModEditor::Controls
{
MaterialControl::MaterialControl(EditorState& state) : m_state(state)
{
}
void MaterialControl::DrawImGui(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
VideoCommon::MaterialData* material,
VideoCommon::CustomAssetLibrary::TimeType* last_data_write,
bool* valid)
{
if (ImGui::BeginTable("MaterialShaderForm", 2))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ID");
ImGui::TableNextColumn();
ImGui::Text("%s", asset_id.c_str());
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Shader");
ImGui::TableNextColumn();
if (AssetDisplay("MaterialShaderAsset", &m_state, &material->shader_asset,
AssetDataType::PixelShader))
{
auto asset = m_state.m_user_data.m_asset_library->GetAssetFromID(material->shader_asset);
if (!asset)
{
ImGui::Text("Please choose a shader for this material");
material->shader_asset = "";
*valid = false;
return;
}
else
{
auto shader = std::get_if<std::unique_ptr<VideoCommon::PixelShaderData>>(&asset->m_data);
if (!shader)
{
ImGui::Text(
"%s",
fmt::format("Asset id '{}' was not type shader!", material->shader_asset).c_str());
material->shader_asset = "";
*valid = false;
return;
}
SetMaterialPropertiesFromShader(*shader->get(), material);
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
*valid = true;
}
}
ImGui::EndTable();
}
if (!*valid)
return;
// Look up shader
auto asset = m_state.m_user_data.m_asset_library->GetAssetFromID(material->shader_asset);
if (!asset)
{
ImGui::Text("Please choose a shader for this material");
}
else
{
auto shader = std::get_if<std::unique_ptr<VideoCommon::PixelShaderData>>(&asset->m_data);
if (!shader)
{
ImGui::Text(
"%s", fmt::format("Asset id '{}' was not type shader!", material->shader_asset).c_str());
}
else
{
VideoCommon::PixelShaderData* shader_data = shader->get();
if (!asset->m_valid)
{
ImGui::Text("%s",
fmt::format("The shader '{}' is invalid!", material->shader_asset).c_str());
}
else
{
DrawControl(asset_id, shader_data, material, last_data_write);
}
}
}
}
void MaterialControl::DrawImGui(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
VideoCommon::RasterMaterialData* material, bool* valid)
{
if (ImGui::BeginTable("MaterialShaderForm", 2))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ID");
ImGui::TableNextColumn();
ImGui::Text("%s", asset_id.c_str());
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Shader");
ImGui::TableNextColumn();
if (AssetDisplay("MaterialShaderAsset", &m_state, &material->shader_asset,
AssetDataType::Shader))
{
auto asset = m_state.m_user_data.m_asset_library->GetAssetFromID(material->shader_asset);
if (!asset)
{
ImGui::Text("Please choose a shader for this material");
material->shader_asset = "";
*valid = false;
return;
}
else
{
auto shader = std::get_if<std::unique_ptr<VideoCommon::RasterShaderData>>(&asset->m_data);
if (!shader)
{
ImGui::Text(
"%s",
fmt::format("Asset id '{}' was not type shader!", material->shader_asset).c_str());
material->shader_asset = "";
*valid = false;
return;
}
SetMaterialPropertiesFromShader(*shader->get(), material);
SetMaterialTexturesFromShader(*shader->get(), material);
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
*valid = true;
}
}
ImGui::EndTable();
}
if (!*valid)
return;
// Look up shader
auto asset = m_state.m_user_data.m_asset_library->GetAssetFromID(material->shader_asset);
if (!asset)
{
ImGui::Text("Please choose a shader for this material");
}
else
{
auto shader = std::get_if<std::unique_ptr<VideoCommon::RasterShaderData>>(&asset->m_data);
if (!shader)
{
ImGui::Text(
"%s", fmt::format("Asset id '{}' was not type shader!", material->shader_asset).c_str());
}
else
{
VideoCommon::RasterShaderData* shader_data = shader->get();
if (!asset->m_valid)
{
ImGui::Text("%s",
fmt::format("The shader '{}' is invalid!", material->shader_asset).c_str());
}
else
{
DrawControl(asset_id, shader_data, material);
}
}
}
}
void MaterialControl::DrawSamplers(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
EditorState* editor_state,
std::span<VideoCommon::RasterShaderData::SamplerData> samplers,
std::span<VideoCommon::TextureSamplerValue> textures)
{
std::size_t texture_sampler_index = 0;
for (const auto& texture_sampler : samplers)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", texture_sampler.name.c_str());
ImGui::TableNextColumn();
auto& texture = textures[texture_sampler_index];
constexpr std::array<GraphicsModEditor::AssetDataType, 2> asset_types_allowed{
AssetDataType::Texture, AssetDataType::RenderTarget};
AssetDataType asset_type_chosen =
texture.is_render_target ? AssetDataType::RenderTarget : AssetDataType::Texture;
if (AssetDisplay(texture_sampler.name, editor_state, &texture.asset, &asset_type_chosen,
asset_types_allowed))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
texture.is_render_target = asset_type_chosen == AssetDataType::RenderTarget;
if (texture.is_render_target)
{
// Camera type TODO
if (texture.camera_type == GraphicsModSystem::CameraType::Specify)
{
if (CameraChoiceControl("Camera Choice", editor_state,
&texture.camera_originating_draw_call))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
}
const auto sampler_origin_str =
VideoCommon::TextureSamplerValue::ToString(texture.sampler_origin);
if (ImGui::BeginCombo(fmt::format("##{}SamplerOrigin", texture_sampler.name).c_str(),
sampler_origin_str.c_str()))
{
static constexpr std::array<VideoCommon::TextureSamplerValue::SamplerOrigin, 2>
all_sampler_types = {VideoCommon::TextureSamplerValue::SamplerOrigin::Asset,
VideoCommon::TextureSamplerValue::SamplerOrigin::TextureHash};
for (const auto& type : all_sampler_types)
{
const bool is_selected = type == texture.sampler_origin;
const auto type_name = VideoCommon::TextureSamplerValue::ToString(type);
if (ImGui::Selectable(fmt::format("{}##{}", type_name, texture_sampler.name).c_str(),
is_selected))
{
texture.sampler_origin = type;
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
ImGui::EndCombo();
}
if (texture.sampler_origin == VideoCommon::TextureSamplerValue::SamplerOrigin::Asset)
{
ImGui::BeginDisabled();
}
if (ImGui::InputText(fmt::format("##{}TextureHash", texture_sampler.name).c_str(),
&texture.texture_hash))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
if (texture.sampler_origin == VideoCommon::TextureSamplerValue::SamplerOrigin::Asset)
{
ImGui::EndDisabled();
}
texture_sampler_index++;
}
}
void MaterialControl::DrawRenderTargets(
const VideoCommon::CustomAssetLibrary::AssetID& asset_id, EditorState* editor_state,
std::span<VideoCommon::RasterShaderData::OutputTargetData> output_targets,
std::vector<VideoCommon::CustomAssetLibrary::AssetID>* render_targets)
{
if (!render_targets) [[unlikely]]
return;
constexpr std::array<GraphicsModEditor::AssetDataType, 1> asset_types_allowed{
AssetDataType::RenderTarget};
AssetDataType asset_type_chosen = AssetDataType::RenderTarget;
for (std::size_t i = 0; i < output_targets.size(); i++)
{
auto& output_target = output_targets[i];
auto& render_target = (*render_targets)[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", output_target.name.c_str());
ImGui::TableNextColumn();
if (AssetDisplay("RenderTargetWindow", editor_state, &render_target, &asset_type_chosen,
asset_types_allowed))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
}
void MaterialControl::DrawUniforms(
const VideoCommon::CustomAssetLibrary::AssetID& asset_id, EditorState*,
const std::map<std::string, VideoCommon::ShaderProperty2>& shader_properties,
std::vector<VideoCommon::MaterialProperty2>* material_properties)
{
std::size_t material_property_index = 0;
for (const auto& entry : shader_properties)
{
// C++20: error with capturing structured bindings for our version of clang
const auto& name = entry.first;
const auto& shader_property = entry.second;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", name.c_str());
ImGui::TableNextColumn();
auto& material_property = (*material_properties)[material_property_index];
std::visit(
overloaded{[&](s32& value) {
if (ImGui::InputInt(fmt::format("##{}", name).c_str(), &value))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<s32, 2>& value) {
if (ImGui::InputInt2(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<s32, 3>& value) {
if (ImGui::InputInt3(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<s32, 4>& value) {
if (ImGui::InputInt4(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](float& value) {
if (ImGui::InputFloat(fmt::format("##{}", name).c_str(), &value))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<float, 2>& value) {
if (ImGui::InputFloat2(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<float, 3>& value) {
if (std::holds_alternative<VideoCommon::ShaderProperty2::RGB>(
shader_property.m_default))
{
if (ImGui::ColorEdit3(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
else
{
if (ImGui::InputFloat3(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
},
[&](std::array<float, 4>& value) {
if (std::holds_alternative<VideoCommon::ShaderProperty2::RGBA>(
shader_property.m_default))
{
if (ImGui::ColorEdit4(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
else
{
if (ImGui::InputFloat4(fmt::format("##{}", name).c_str(), value.data()))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
},
[&](bool& value) {
if (ImGui::Checkbox(fmt::format("##{}", name).c_str(), &value))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}},
material_property.m_value);
material_property_index++;
}
}
void MaterialControl::DrawProperties(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
EditorState*, VideoCommon::RasterMaterialData* material)
{
if (ImGui::CollapsingHeader("Blending", ImGuiTreeNodeFlags_DefaultOpen))
{
bool custom_blending = material->blending_state.has_value();
if (ImGui::Checkbox("Use Custom Data", &custom_blending))
{
if (custom_blending && !material->blending_state)
{
material->blending_state = BlendingState{};
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
else if (!custom_blending && material->blending_state)
{
material->blending_state.reset();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
if (material->blending_state && ImGui::BeginTable("BlendingForm", 2))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Blend Enable");
ImGui::TableNextColumn();
bool blend_enable = material->blending_state->blendenable == 0;
if (ImGui::Checkbox("##BlendEnable", &blend_enable))
{
material->blending_state->blendenable = static_cast<u32>(blend_enable);
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Logicop Enable");
ImGui::TableNextColumn();
bool logicop_enable = material->blending_state->logicopenable == 0;
if (ImGui::Checkbox("##LogicOpEnable", &logicop_enable))
{
material->blending_state->logicopenable = static_cast<u32>(logicop_enable);
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Color Update");
ImGui::TableNextColumn();
bool colorupdate = material->blending_state->colorupdate == 0;
if (ImGui::Checkbox("##ColorUpdate", &colorupdate))
{
material->blending_state->colorupdate = static_cast<u32>(colorupdate);
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Alpha Update");
ImGui::TableNextColumn();
bool alphaupdate = material->blending_state->alphaupdate == 0;
if (ImGui::Checkbox("##AlphaUpdate", &alphaupdate))
{
material->blending_state->alphaupdate = static_cast<u32>(alphaupdate);
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Culling", ImGuiTreeNodeFlags_DefaultOpen))
{
bool custom_cull_mode = material->cull_mode.has_value();
if (ImGui::Checkbox("Use Custom Data##2", &custom_cull_mode))
{
if (custom_cull_mode && !material->cull_mode)
{
material->cull_mode = CullMode{};
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
else if (!custom_cull_mode && material->cull_mode)
{
material->cull_mode.reset();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
if (material->cull_mode && ImGui::BeginTable("CullingForm", 2))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Cull Mode");
ImGui::TableNextColumn();
if (ImGui::BeginCombo("##CullMode", fmt::to_string(*material->cull_mode).c_str()))
{
static constexpr std::array<CullMode, 4> all_cull_modes = {CullMode::None, CullMode::Back,
CullMode::Front, CullMode::All};
for (const auto& cull_mode : all_cull_modes)
{
const bool is_selected = cull_mode == *material->cull_mode;
const auto cull_mode_str = fmt::to_string(cull_mode);
if (ImGui::Selectable(fmt::format("{}##", cull_mode_str).c_str(), is_selected))
{
material->cull_mode = cull_mode;
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
ImGui::EndCombo();
}
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Depth Test", ImGuiTreeNodeFlags_DefaultOpen))
{
bool custom_depth = material->depth_state.has_value();
if (ImGui::Checkbox("Use Custom Data##3", &custom_depth))
{
if (custom_depth && !material->depth_state)
{
material->depth_state = DepthState{};
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
else if (!custom_depth && material->depth_state)
{
material->depth_state.reset();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
if (material->depth_state && ImGui::BeginTable("DepthForm", 2))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Test Enable");
ImGui::TableNextColumn();
bool test_enable = static_cast<bool>(material->depth_state->testenable);
if (ImGui::Checkbox("##TestEnable", &test_enable))
{
material->depth_state->testenable = static_cast<u32>(test_enable);
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Update Enable");
ImGui::TableNextColumn();
bool update_enable = static_cast<bool>(material->depth_state->updateenable);
if (ImGui::Checkbox("##UpdateEnable", &update_enable))
{
material->depth_state->updateenable = static_cast<u32>(update_enable);
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Comparison");
ImGui::TableNextColumn();
if (ImGui::BeginCombo("##CompareMode", fmt::to_string(material->depth_state->func).c_str()))
{
static constexpr std::array<CompareMode, 8> all_compare_modes = {
CompareMode::Never, CompareMode::Less, CompareMode::Equal, CompareMode::LEqual,
CompareMode::Greater, CompareMode::NEqual, CompareMode::GEqual, CompareMode::Always};
for (const auto& compare_mode : all_compare_modes)
{
const bool is_selected = compare_mode == material->depth_state->func;
const auto compare_mode_str = fmt::to_string(compare_mode);
if (ImGui::Selectable(fmt::format("{}##", compare_mode_str).c_str(), is_selected))
{
material->depth_state->func = compare_mode;
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
ImGui::EndCombo();
}
ImGui::EndTable();
}
}
}
void MaterialControl::DrawLinkedMaterial(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
EditorState* editor_state,
VideoCommon::RasterMaterialData* material)
{
if (AssetDisplay("Next Material", editor_state, &material->next_material_asset,
AssetDataType::RasterMaterial))
{
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
void MaterialControl::DrawControl(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
VideoCommon::PixelShaderData* shader,
VideoCommon::MaterialData* material,
VideoCommon::CustomAssetLibrary::TimeType* last_data_write)
{
if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen))
{
if (ImGui::BeginTable("MaterialPropertiesForm", 2))
{
std::size_t material_property_index = 0;
for (const auto& entry : shader->m_properties)
{
// C++20: error with capturing structured bindings for our version of clang
const auto& name = entry.first;
const auto& shader_property = entry.second;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", name.c_str());
ImGui::TableNextColumn();
auto& material_property = material->properties[material_property_index];
std::visit(
overloaded{
[&](VideoCommon::TextureSamplerValue& value) {
if (AssetDisplay(material_property.m_code_name, &m_state, &value.asset,
AssetDataType::Texture))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
const auto sampler_origin_str =
VideoCommon::TextureSamplerValue::ToString(value.sampler_origin);
if (ImGui::BeginCombo(
fmt::format("##{}SamplerOrigin", material_property.m_code_name).c_str(),
sampler_origin_str.c_str()))
{
static std::array<VideoCommon::TextureSamplerValue::SamplerOrigin, 2>
all_sampler_types = {
VideoCommon::TextureSamplerValue::SamplerOrigin::Asset,
VideoCommon::TextureSamplerValue::SamplerOrigin::TextureHash};
for (const auto& type : all_sampler_types)
{
const bool is_selected = type == value.sampler_origin;
const auto type_name = VideoCommon::TextureSamplerValue::ToString(type);
if (ImGui::Selectable(
fmt::format("{}##{}", type_name, material_property.m_code_name)
.c_str(),
is_selected))
{
value.sampler_origin = type;
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
ImGui::EndCombo();
}
if (value.sampler_origin ==
VideoCommon::TextureSamplerValue::SamplerOrigin::Asset)
{
ImGui::BeginDisabled();
}
if (ImGui::InputText(
fmt::format("##{}TextureHash", material_property.m_code_name).c_str(),
&value.texture_hash))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
if (value.sampler_origin ==
VideoCommon::TextureSamplerValue::SamplerOrigin::Asset)
{
ImGui::EndDisabled();
}
},
[&](s32& value) {
if (ImGui::InputInt(fmt::format("##{}", material_property.m_code_name).c_str(),
&value))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<s32, 2>& value) {
if (ImGui::InputInt2(fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<s32, 3>& value) {
if (ImGui::InputInt3(fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<s32, 4>& value) {
if (ImGui::InputInt4(fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](float& value) {
if (ImGui::InputFloat(fmt::format("##{}", material_property.m_code_name).c_str(),
&value))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<float, 2>& value) {
if (ImGui::InputFloat2(fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
},
[&](std::array<float, 3>& value) {
if (std::holds_alternative<VideoCommon::ShaderProperty::RGB>(
shader_property.m_default))
{
if (ImGui::ColorEdit3(
fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
else
{
if (ImGui::InputFloat3(
fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
},
[&](std::array<float, 4>& value) {
if (std::holds_alternative<VideoCommon::ShaderProperty::RGBA>(
shader_property.m_default))
{
if (ImGui::ColorEdit4(
fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
else
{
if (ImGui::InputFloat4(
fmt::format("##{}", material_property.m_code_name).c_str(),
value.data()))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}
},
[&](bool& value) {
if (ImGui::Checkbox(fmt::format("##{}", material_property.m_code_name).c_str(),
&value))
{
*last_data_write = std::chrono::system_clock::now();
GraphicsModEditor::EditorEvents::AssetReloadEvent::Trigger(asset_id);
}
}},
material_property.m_value);
material_property_index++;
}
ImGui::EndTable();
}
}
}
void MaterialControl::DrawControl(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
VideoCommon::RasterShaderData* shader,
VideoCommon::RasterMaterialData* material)
{
if (ImGui::CollapsingHeader("Input", ImGuiTreeNodeFlags_DefaultOpen))
{
if (ImGui::BeginTable("MaterialVertexPropertiesForm", 2))
{
DrawUniforms(asset_id, &m_state, shader->m_vertex_properties, &material->vertex_properties);
ImGui::EndTable();
}
if (ImGui::BeginTable("MaterialPixelPropertiesForm", 2))
{
DrawUniforms(asset_id, &m_state, shader->m_pixel_properties, &material->pixel_properties);
DrawSamplers(asset_id, &m_state, shader->m_pixel_samplers, material->pixel_textures);
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Output", ImGuiTreeNodeFlags_DefaultOpen))
{
if (ImGui::BeginTable("MaterialRenderTargetsForm", 2))
{
DrawRenderTargets(asset_id, &m_state, shader->m_output_targets, &material->render_targets);
ImGui::EndTable();
}
}
DrawProperties(asset_id, &m_state, material);
DrawLinkedMaterial(asset_id, &m_state, material);
}
} // namespace GraphicsModEditor::Controls

Some files were not shown because too many files have changed in this diff Show more