Merge 4d875245cb
into 049e52ce1c
3
.gitmodules
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"pixel_properties":[],
|
||||
"pixel_output_targets":[],
|
||||
"pixel_samplers":[],
|
||||
"vertex_properties":[]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
|
||||
{
|
||||
dolphin_emulated_vertex(vertex_input, vertex_output);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"next_material_asset":"",
|
||||
"pixel_properties":[],
|
||||
"pixel_textures":[],
|
||||
"render_targets":[],
|
||||
"shader_asset": "highlight_shader",
|
||||
"vertex_properties":[]
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"next_material_asset":"",
|
||||
"pixel_properties":[],
|
||||
"pixel_textures":[],
|
||||
"render_targets":[],
|
||||
"shader_asset": "simple_light_visualization_shader",
|
||||
"vertex_properties":[]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"pixel_properties":[],
|
||||
"pixel_output_targets":[],
|
||||
"pixel_samplers":[],
|
||||
"vertex_properties":[]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
|
||||
{
|
||||
dolphin_emulated_vertex(vertex_input, vertex_output);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"next_material_asset":"",
|
||||
"pixel_properties":[],
|
||||
"pixel_textures":[],
|
||||
"render_targets":[],
|
||||
"shader_asset": "normal_visualization_shader",
|
||||
"vertex_properties":[]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"pixel_properties":[],
|
||||
"pixel_output_targets":[],
|
||||
"pixel_samplers":[],
|
||||
"vertex_properties":[]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
|
||||
{
|
||||
dolphin_emulated_vertex(vertex_input, vertex_output);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"shader_asset": "",
|
||||
"pixel_properties": [],
|
||||
"vertex_properties": [],
|
||||
"next_material_asset": "",
|
||||
"pixel_textures": [],
|
||||
"render_targets": []
|
||||
}
|
6
Data/Sys/GraphicsModEditor/Templates/raster_shader.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"pixel_properties": [],
|
||||
"pixel_output_targets": [],
|
||||
"pixel_samplers": [],
|
||||
"vertex_properties": []
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
void fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput frag_output)
|
||||
{
|
||||
dolphin_emulated_fragment(frag_input, frag_output);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
void vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
|
||||
{
|
||||
dolphin_emulated_vertex(vertex_input, vertex_output);
|
||||
}
|
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-code-file-100.png
Normal file
After Width: | Height: | Size: 813 B |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-cube-filled-50.png
Normal file
After Width: | Height: | Size: 698 B |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-document-500.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-error-50.png
Normal file
After Width: | Height: | Size: 602 B |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-file-500.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-folder-50.png
Normal file
After Width: | Height: | Size: 245 B |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-folder-500.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-image-file-500.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-portraits-50.png
Normal file
After Width: | Height: | Size: 750 B |
BIN
Data/Sys/GraphicsModEditor/Textures/icons8-search-50.png
Normal file
After Width: | Height: | Size: 724 B |
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"shader_asset": "bloom_shader",
|
||||
"values": []
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"properties": []
|
||||
}
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"shader_asset": "dof_shader",
|
||||
"values": []
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"properties": []
|
||||
}
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
1
Externals/imgui/CMakeLists.txt
vendored
|
@ -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})
|
||||
|
|
2
Externals/imgui/imconfig.h
vendored
|
@ -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
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ add_library(common
|
|||
IOFile.h
|
||||
JitRegister.cpp
|
||||
JitRegister.h
|
||||
JsonUtil.cpp
|
||||
JsonUtil.h
|
||||
JsonUtil.cpp
|
||||
Lazy.h
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
175
Source/Core/VideoCommon/Assets/CustomAssetLoader2.cpp
Normal 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
|
65
Source/Core/VideoCommon/Assets/CustomAssetLoader2.h
Normal 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
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
200
Source/Core/VideoCommon/Assets/MaterialAssetUtils.cpp
Normal 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
|
19
Source/Core/VideoCommon/Assets/MaterialAssetUtils.h
Normal 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
|
43
Source/Core/VideoCommon/Assets/RenderTargetAsset.cpp
Normal 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
|
31
Source/Core/VideoCommon/Assets/RenderTargetAsset.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
77
Source/Core/VideoCommon/Assets/TextureSamplerValue.cpp
Normal 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
|
35
Source/Core/VideoCommon/Assets/TextureSamplerValue.h
Normal 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
|
13
Source/Core/VideoCommon/Assets/Types.h
Normal 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>;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|