4874 lines
126 KiB
HTML
4874 lines
126 KiB
HTML
|
<!-- Copyright Epic Games, Inc. All Rights Reserved. -->
|
||
|
<html>
|
||
|
<title>GPU Dump Viewer</title>
|
||
|
|
||
|
<script type="text/javascript">
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- CONSTANTS
|
||
|
|
||
|
const k_current_dir = "./";
|
||
|
|
||
|
const k_scroll_width = 8;
|
||
|
const k_null_json_ptr = "0000000000000000";
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- GLOBALS
|
||
|
|
||
|
var g_dump_service = {};
|
||
|
var g_infos = {};
|
||
|
var g_dump_cvars = {};
|
||
|
var g_passes = [];
|
||
|
var g_descs = {};
|
||
|
|
||
|
var g_view = null;
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- GLOBALS
|
||
|
|
||
|
class IView
|
||
|
{
|
||
|
constructor()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
setup_html(parent)
|
||
|
{
|
||
|
parent.innerHTML = '';
|
||
|
}
|
||
|
|
||
|
resize(ctx)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
release()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function set_main_view(new_view)
|
||
|
{
|
||
|
var parent_dom = document.getElementById('main_right_pannel');
|
||
|
if (g_view !== null)
|
||
|
{
|
||
|
g_view.release();
|
||
|
delete g_view;
|
||
|
parent_dom.innerHTML = '';
|
||
|
}
|
||
|
g_view = new_view;
|
||
|
if (g_view !== null)
|
||
|
{
|
||
|
g_view.setup_html(parent_dom);
|
||
|
onresize_body();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- FILE LOADING
|
||
|
|
||
|
function does_file_exists(relative_path)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
var request = new XMLHttpRequest();
|
||
|
request.open('HEAD', k_current_dir + relative_path, false);
|
||
|
request.send(null);
|
||
|
return request.status != 404;
|
||
|
}
|
||
|
catch (error)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function load_text_file(relative_path)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
var request = new XMLHttpRequest();
|
||
|
request.open('GET', k_current_dir + relative_path, false);
|
||
|
request.send(null);
|
||
|
if (request.status === 0 || request.status === 200)
|
||
|
{
|
||
|
return request.responseText;
|
||
|
}
|
||
|
add_console_event('error', `couldn't load ${relative_path}: Returned HTTP status ${request.status}`);
|
||
|
}
|
||
|
catch (error)
|
||
|
{
|
||
|
add_console_event('error', `couldn't load ${relative_path}: ${error}`);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
function load_binary_file(relative_path, callback)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
var request = new XMLHttpRequest();
|
||
|
request.open('GET', k_current_dir + relative_path, true);
|
||
|
request.responseType = "arraybuffer";
|
||
|
|
||
|
request.onload = function(event)
|
||
|
{
|
||
|
var array_buffer = request.response;
|
||
|
if ((request.status === 0 || request.status === 200) && array_buffer)
|
||
|
{
|
||
|
callback(array_buffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
add_console_event('error', `couldn't load ${relative_path}: Returned HTTP status ${request.status}`);
|
||
|
callback(null);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
request.onerror = function() {
|
||
|
add_console_event('error', `couldn't load ${relative_path}`);
|
||
|
callback(null);
|
||
|
};
|
||
|
|
||
|
request.send(null);
|
||
|
}
|
||
|
catch (error)
|
||
|
{
|
||
|
add_console_event('error', `couldn't load ${relative_path}: ${error}`);
|
||
|
callback(null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function load_resource_binary_file(relative_path, callback)
|
||
|
{
|
||
|
return load_binary_file(relative_path, function(raw_texture_data)
|
||
|
{
|
||
|
var compression_type = '';
|
||
|
if (g_dump_service['CompressionName'] == 'Zlib')
|
||
|
{
|
||
|
compression_type = 'deflate';
|
||
|
}
|
||
|
else if (g_dump_service['CompressionName'] == 'GZip')
|
||
|
{
|
||
|
compression_type = 'gzip';
|
||
|
}
|
||
|
|
||
|
if (raw_texture_data === null || compression_type == '')
|
||
|
{
|
||
|
return callback(raw_texture_data);
|
||
|
}
|
||
|
|
||
|
var decompressor = new DecompressionStream(compression_type);
|
||
|
var decompressed_stream = new Blob([raw_texture_data]).stream().pipeThrough(decompressor);
|
||
|
new Response(decompressed_stream).arrayBuffer().then(callback, function() { callback(null); });
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function load_json(relative_path)
|
||
|
{
|
||
|
var txt = load_text_file(relative_path);
|
||
|
|
||
|
if (txt === null)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return JSON.parse(load_text_file(relative_path));
|
||
|
}
|
||
|
|
||
|
function load_json_dict_sequence(relative_path)
|
||
|
{
|
||
|
var text_file = load_text_file(relative_path);
|
||
|
if (text_file == '')
|
||
|
{
|
||
|
return new Array();
|
||
|
}
|
||
|
var dicts = text_file.split("}{");
|
||
|
var dict_sequence = [];
|
||
|
dicts.forEach(function(value, index, array) {
|
||
|
if (!value.startsWith('{'))
|
||
|
{
|
||
|
value = '{' + value;
|
||
|
}
|
||
|
if (!value.endsWith('}'))
|
||
|
{
|
||
|
value = value + '}';
|
||
|
}
|
||
|
dict_sequence.push(JSON.parse(value));
|
||
|
});
|
||
|
return dict_sequence;
|
||
|
}
|
||
|
|
||
|
function get_resource_desc(unique_resource_name)
|
||
|
{
|
||
|
return g_descs[unique_resource_name];
|
||
|
}
|
||
|
|
||
|
function load_structure_metadata(structure_ptr)
|
||
|
{
|
||
|
var cache = {};
|
||
|
|
||
|
function load_nested_structure_metadata(nested_structure_ptr)
|
||
|
{
|
||
|
if (nested_structure_ptr in cache)
|
||
|
{
|
||
|
return cache[nested_structure_ptr];
|
||
|
}
|
||
|
|
||
|
var metadata = load_json(`StructuresMetadata/${nested_structure_ptr}.json`);
|
||
|
cache[nested_structure_ptr] = metadata;
|
||
|
|
||
|
for (var member of metadata['Members'])
|
||
|
{
|
||
|
if (member['StructMetadata'] == k_null_json_ptr)
|
||
|
{
|
||
|
member['StructMetadata'] = null;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
member['StructMetadata'] = load_nested_structure_metadata(member['StructMetadata']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return metadata;
|
||
|
}
|
||
|
|
||
|
return load_nested_structure_metadata(structure_ptr);
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- UTILITY
|
||
|
|
||
|
function get_filename(file_path)
|
||
|
{
|
||
|
return file_path.split(/(\\|\/)/g).pop();
|
||
|
}
|
||
|
|
||
|
function px_string_to_int(str)
|
||
|
{
|
||
|
if (Number.isInteger(str))
|
||
|
{
|
||
|
return str;
|
||
|
}
|
||
|
if (str.endsWith('px'))
|
||
|
{
|
||
|
return Number(str.substring(0, str.length - 2));
|
||
|
}
|
||
|
return Number(str);
|
||
|
}
|
||
|
|
||
|
function parse_subresource_unique_name(subresource_unique_name)
|
||
|
{
|
||
|
var splitted_name = subresource_unique_name.split(".");
|
||
|
|
||
|
var subresource_info = {};
|
||
|
subresource_info['subresource'] = null;
|
||
|
subresource_info['array_slice'] = null;
|
||
|
|
||
|
if (splitted_name[splitted_name.length - 1].startsWith('mip') || splitted_name[splitted_name.length - 1] == 'stencil')
|
||
|
{
|
||
|
subresource_info['subresource'] = splitted_name.pop();
|
||
|
}
|
||
|
|
||
|
if (splitted_name[splitted_name.length - 1].startsWith('[') && splitted_name[splitted_name.length - 1].endsWith(']'))
|
||
|
{
|
||
|
var array_slice_bracket = splitted_name.pop();
|
||
|
subresource_info['array_slice'] = parseInt(array_slice_bracket.substring(1, array_slice_bracket.length - 1));
|
||
|
}
|
||
|
|
||
|
subresource_info['resource'] = splitted_name.join('.');
|
||
|
|
||
|
return subresource_info;
|
||
|
}
|
||
|
|
||
|
function get_subresource_unique_name(subresource_info)
|
||
|
{
|
||
|
var subresource_unique_name = subresource_info['resource'];
|
||
|
if (subresource_info['array_slice'] !== null)
|
||
|
{
|
||
|
subresource_unique_name += `.[${subresource_info['array_slice']}]`;
|
||
|
}
|
||
|
if (subresource_info['subresource'] !== null)
|
||
|
{
|
||
|
subresource_unique_name += `.${subresource_info['subresource']}`;
|
||
|
}
|
||
|
return subresource_unique_name;
|
||
|
}
|
||
|
|
||
|
function prettify_subresource_unique_name(subresource_info, resource_desc)
|
||
|
{
|
||
|
if (!resource_desc)
|
||
|
{
|
||
|
return get_subresource_unique_name(subresource_info);
|
||
|
}
|
||
|
|
||
|
var name = resource_desc['Name'];
|
||
|
|
||
|
if (subresource_info['array_slice'] !== null)
|
||
|
{
|
||
|
name += ` slice[${subresource_info['array_slice']}]`;
|
||
|
}
|
||
|
|
||
|
if (subresource_info['subresource'] !== null && (subresource_info['subresource'] == 'stencil' || resource_desc['NumMips'] > 1))
|
||
|
{
|
||
|
name += ` ${subresource_info['subresource']}`;
|
||
|
}
|
||
|
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
function parse_subresource_unique_version_name(subresource_unique_version_name)
|
||
|
{
|
||
|
var splitted_name = subresource_unique_version_name.split(".");
|
||
|
|
||
|
var pass_ptr = -1;
|
||
|
var draw_id = -1;
|
||
|
|
||
|
var last = splitted_name.pop();
|
||
|
if (last.startsWith('d'))
|
||
|
{
|
||
|
draw_id = parseInt(last.substring(1));
|
||
|
last = splitted_name.pop();
|
||
|
}
|
||
|
|
||
|
if (last.startsWith('v'))
|
||
|
{
|
||
|
pass_ptr = last.substring(1);
|
||
|
}
|
||
|
|
||
|
var subresource_unique_name = splitted_name.join('.');
|
||
|
var subresource_version_info = parse_subresource_unique_name(subresource_unique_name);
|
||
|
|
||
|
subresource_version_info['pass'] = pass_ptr;
|
||
|
subresource_version_info['draw'] = draw_id;
|
||
|
return subresource_version_info;
|
||
|
}
|
||
|
|
||
|
function get_subresource_unique_version_name(subresource_version_info)
|
||
|
{
|
||
|
if (subresource_version_info['draw'] >= 0)
|
||
|
{
|
||
|
return `${get_subresource_unique_name(subresource_version_info)}.v${subresource_version_info['pass']}.d${subresource_version_info['draw']}`;
|
||
|
}
|
||
|
return `${get_subresource_unique_name(subresource_version_info)}.v${subresource_version_info['pass']}`;
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- SHADER PARAMETERS
|
||
|
|
||
|
function iterate_structure_members(root_structure_metadata, callback)
|
||
|
{
|
||
|
function iterate_recursive(structure_metadata, offset, cpp_prefix, shader_prefix)
|
||
|
{
|
||
|
for (var member of structure_metadata['Members'])
|
||
|
{
|
||
|
var base_type = member['BaseType'];
|
||
|
|
||
|
if (base_type == 'UBMT_NESTED_STRUCT')
|
||
|
{
|
||
|
iterate_recursive(member['StructMetadata'], offset + member['Offset'], cpp_prefix + member['Name'] + '.', shader_prefix + member['Name'] + '_');
|
||
|
}
|
||
|
else if (base_type == 'UBMT_INCLUDED_STRUCT')
|
||
|
{
|
||
|
iterate_recursive(member['StructMetadata'], offset + member['Offset'], cpp_prefix + member['Name'] + '.', shader_prefix);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var params = {
|
||
|
member: member,
|
||
|
base_type: base_type,
|
||
|
byte_offset: offset + member['Offset'],
|
||
|
cpp_name: cpp_prefix + member['Name'],
|
||
|
shader_name: shader_prefix + member['Name'],
|
||
|
};
|
||
|
callback(params);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iterate_recursive(root_structure_metadata, /* offset = */ 0, /* cpp_prefix = */ '', /* shader_prefix = */ '');
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- FLOAT ENCODING
|
||
|
|
||
|
function decode_float(raw, total_bit_count, exp_bit_count, has_sign)
|
||
|
{
|
||
|
var exp_bias = (1 << (exp_bit_count - 1)) - 1;
|
||
|
var mantissa_bit_count = total_bit_count - exp_bit_count - (has_sign ? 1 : 0);
|
||
|
|
||
|
var sign_bit = (raw >> (total_bit_count - 1)) & 0x1;
|
||
|
var mantissa_bits = (raw >> 0) & ((0x1 << mantissa_bit_count) - 1);
|
||
|
var exp_bits = (raw >> mantissa_bit_count) & ((0x1 << exp_bit_count) - 1);
|
||
|
|
||
|
var is_max_exp = exp_bits == ((0x1 << exp_bit_count) - 1);
|
||
|
|
||
|
var is_denormal = exp_bits == 0;
|
||
|
var is_infinity = is_max_exp && mantissa_bits == 0;
|
||
|
var is_nan = is_max_exp && mantissa_bits != 0;
|
||
|
|
||
|
var exp = exp_bits - exp_bias;
|
||
|
var mantissa = mantissa_bits * Math.pow(0.5, mantissa_bit_count);
|
||
|
var sign = (has_sign && (sign_bit == 1)) ? -1 : 1;
|
||
|
|
||
|
if (is_nan)
|
||
|
{
|
||
|
return 'nan';
|
||
|
}
|
||
|
else if (is_infinity)
|
||
|
{
|
||
|
return sign == -1 ? '-inf' : '+inf';
|
||
|
}
|
||
|
else if (is_denormal)
|
||
|
{
|
||
|
var value = sign * mantissa * Math.pow(0.5, exp_bias - 1);
|
||
|
return value;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var value = sign * (1.0 + mantissa) * Math.pow(2.0, exp);
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function decode_float10(raw)
|
||
|
{
|
||
|
return decode_float(raw, /* total_bit_count = */ 10, /* exp_bit_count = */ 5, /* has_sign = */ false);
|
||
|
}
|
||
|
|
||
|
function decode_float11(raw)
|
||
|
{
|
||
|
return decode_float(raw, /* total_bit_count = */ 11, /* exp_bit_count = */ 5, /* has_sign = */ false);
|
||
|
}
|
||
|
|
||
|
function decode_float16(raw)
|
||
|
{
|
||
|
return decode_float(raw, /* total_bit_count = */ 16, /* exp_bit_count = */ 5, /* has_sign = */ true);
|
||
|
}
|
||
|
|
||
|
function decode_float32(raw)
|
||
|
{
|
||
|
return decode_float(raw, /* total_bit_count = */ 32, /* exp_bit_count = */ 8, /* has_sign = */ true);
|
||
|
}
|
||
|
|
||
|
function test_decode_float()
|
||
|
{
|
||
|
var tests = [
|
||
|
// Zero
|
||
|
[0x0000, 0.0],
|
||
|
[0x8000, -0.0],
|
||
|
|
||
|
// normals
|
||
|
[0x4000, 2.0],
|
||
|
[0xc000, -2.0],
|
||
|
|
||
|
// denormals
|
||
|
[0x0001, 5.960464477539063e-8],
|
||
|
[0x8001, -5.960464477539063e-8],
|
||
|
|
||
|
// exotics
|
||
|
[0x7c00, '+inf'],
|
||
|
[0xFc00, '-inf'],
|
||
|
[0x7c01, 'nan'],
|
||
|
[0xFc01, 'nan'],
|
||
|
];
|
||
|
|
||
|
for (var i = 0; i < tests.length; i++)
|
||
|
{
|
||
|
var encoded = tests[i][0];
|
||
|
var ref = tests[i][1];
|
||
|
var computed = decode_float16(encoded);
|
||
|
|
||
|
console.assert(computed == ref);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY PASS
|
||
|
|
||
|
function display_pass_hierarchy()
|
||
|
{
|
||
|
var search_pass = document.getElementById('pass_search_input').value;
|
||
|
var search_resource = document.getElementById('resource_search_input').value;
|
||
|
var html = '';
|
||
|
var parent_event_scopes = [];
|
||
|
|
||
|
g_passes.forEach(function(pass_data, pass_id) {
|
||
|
var pass_event_scopes = pass_data['ParentEventScopes'];
|
||
|
|
||
|
var show_pass = true;
|
||
|
if (search_pass != '')
|
||
|
{
|
||
|
show_pass = pass_data['EventName'].toLowerCase().includes(search_pass.toLowerCase());
|
||
|
|
||
|
for (var i = 0; i < pass_event_scopes.length; i++)
|
||
|
{
|
||
|
show_pass = show_pass || pass_event_scopes[i].toLowerCase().includes(search_pass.toLowerCase());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var show_resource = true;
|
||
|
if (search_resource != '')
|
||
|
{
|
||
|
show_resource = false;
|
||
|
for (var subresource_unique_name of pass_data['InputResources'])
|
||
|
{
|
||
|
var resource_unique_name = parse_subresource_unique_name(subresource_unique_name)['resource'];
|
||
|
var resource_name = get_resource_desc(resource_unique_name)['Name'];
|
||
|
show_resource = show_resource || resource_name.toLowerCase().includes(search_resource.toLowerCase());
|
||
|
}
|
||
|
for (var subresource_unique_name of pass_data['OutputResources'])
|
||
|
{
|
||
|
var resource_unique_name = parse_subresource_unique_name(subresource_unique_name)['resource'];
|
||
|
var resource_name = get_resource_desc(resource_unique_name)['Name'];
|
||
|
show_resource = show_resource || resource_name.toLowerCase().includes(search_resource.toLowerCase());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var has_input_or_outputs = pass_data['InputResources'].length > 0 || pass_data['OutputResources'].length > 0;
|
||
|
|
||
|
if (show_pass && show_resource && has_input_or_outputs)
|
||
|
{
|
||
|
var shared_scope = 0;
|
||
|
for (var i = 0; i < Math.min(parent_event_scopes.length, pass_event_scopes.length); i++)
|
||
|
{
|
||
|
if (parent_event_scopes[i] == pass_event_scopes[pass_event_scopes.length - 1 - i])
|
||
|
{
|
||
|
shared_scope++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
parent_event_scopes = parent_event_scopes.slice(0, shared_scope);
|
||
|
|
||
|
for (var i = shared_scope; i < pass_event_scopes.length; i++)
|
||
|
{
|
||
|
var scope = pass_event_scopes[pass_event_scopes.length - 1 - i];
|
||
|
html += `<a style="padding-left: ${10 + 16 * i}px;" class="disabled">${scope}</a>`;
|
||
|
parent_event_scopes.push(scope);
|
||
|
}
|
||
|
|
||
|
html += `<a style="padding-left: ${10 + 16 * pass_event_scopes.length}px;" href="#display_pass(${pass_id});">${pass_data['EventName']}</a>`;
|
||
|
}
|
||
|
});
|
||
|
document.getElementById('pass_hierarchy').innerHTML = html;
|
||
|
update_href_selection(document.getElementById('pass_hierarchy'));
|
||
|
}
|
||
|
|
||
|
class ResourceView extends IView
|
||
|
{
|
||
|
constructor(subresource_version_info, resource_desc)
|
||
|
{
|
||
|
super();
|
||
|
this.subresource_version_info = subresource_version_info;
|
||
|
this.resource_desc = resource_desc;
|
||
|
this.onload = function() { };
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
return [];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class PassView extends IView
|
||
|
{
|
||
|
constructor(pass_id)
|
||
|
{
|
||
|
super();
|
||
|
this.pass_id = pass_id;
|
||
|
this.pass_data = g_passes[pass_id];
|
||
|
this.pass_draws_data = [];
|
||
|
this.resource_view = null;
|
||
|
|
||
|
if (this.pass_data['DrawCount'] > 0)
|
||
|
{
|
||
|
this.pass_draws_data = load_json_dict_sequence(`Passes/Pass.${this.pass_data['Pointer']}.Draws.json`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
var column_width = '50%';
|
||
|
var draw_column_display = 'none';
|
||
|
|
||
|
if (this.pass_draws_data.length > 0)
|
||
|
{
|
||
|
column_width = '33%';
|
||
|
draw_column_display = 'block';
|
||
|
}
|
||
|
|
||
|
parent_dom.innerHTML = `
|
||
|
<div class="pass_title main_div">
|
||
|
${this.pass_data['EventName']}
|
||
|
<div class="button_list" style="display:inline-block;"><a href="#display_pass_parameters(${this.pass_id});">PassParameters</a></div>
|
||
|
</div>
|
||
|
<table width="100%">
|
||
|
<tr>
|
||
|
<td width="${column_width}">
|
||
|
<div class="main_div">
|
||
|
<div class="selection_list_title">Input resources</div>
|
||
|
<div class="selection_list_search">
|
||
|
<input type="search" id="pass_input_resource_search" oninput="g_view.refresh_input_resource_list();" onchange="g_view.refresh_input_resource_list();" placeholder="Search input resource..." />
|
||
|
</div>
|
||
|
<div class="selection_list" id="pass_input_resource_list"></div>
|
||
|
</div>
|
||
|
</td>
|
||
|
<td width="${column_width}">
|
||
|
<div class="main_div">
|
||
|
<div class="selection_list_title">Output resources</div>
|
||
|
<div class="selection_list_search">
|
||
|
<input type="search" id="pass_output_resource_search" oninput="g_view.refresh_output_resource_list();" onchange="g_view.refresh_output_resource_list();" placeholder="Search output resource..." />
|
||
|
</div>
|
||
|
<div class="selection_list" id="pass_output_resource_list"></div>
|
||
|
</div>
|
||
|
</td>
|
||
|
<td width="${column_width}">
|
||
|
<div class="main_div" style="display: ${draw_column_display};">
|
||
|
<div class="selection_list_title">Draws</div>
|
||
|
<div class="selection_list_search">
|
||
|
<input type="search" id="pass_draw_search" oninput="g_view.refresh_draw_resource_list();" onchange="g_view.refresh_draw_resource_list();" placeholder="Search draw..." />
|
||
|
</div>
|
||
|
<div class="selection_list" id="pass_draw_list"></div>
|
||
|
</div>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
<div id="display_resource_pannel"></div>
|
||
|
<table width="100%">
|
||
|
<tr>
|
||
|
<td width="50%">
|
||
|
<div class="main_div" id="resource_pass_modifying_outter">
|
||
|
<div class="selection_list_title" id="resource_pass_modifying_title"></div>
|
||
|
<div class="selection_list_search">
|
||
|
<input type="search" id="resource_pass_modifying_search" oninput="g_view.refresh_resource_modifying_list();" onchange="g_view.refresh_resource_modifying_list();" placeholder="Search modifying pass..." />
|
||
|
</div>
|
||
|
<div class="selection_list" id="resource_pass_modifying_list"></div>
|
||
|
</div>
|
||
|
</td>
|
||
|
<td width="50%">
|
||
|
<div class="main_div" id="resource_pass_reading_outter">
|
||
|
<div class="selection_list_title" id="resource_pass_reading_title"></div>
|
||
|
<div class="selection_list_search">
|
||
|
<input type="search" id="resource_pass_reading_search" oninput="g_view.refresh_resource_reading_list();" onchange="g_view.refresh_resource_reading_list();" placeholder="Search reading pass..." />
|
||
|
</div>
|
||
|
<div class="selection_list" id="resource_pass_reading_list"></div>
|
||
|
</div>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
`;
|
||
|
}
|
||
|
|
||
|
refresh_all_lists()
|
||
|
{
|
||
|
this.refresh_input_resource_list();
|
||
|
this.refresh_output_resource_list();
|
||
|
this.refresh_draw_resource_list();
|
||
|
this.refresh_resource_modifying_list();
|
||
|
this.refresh_resource_reading_list();
|
||
|
}
|
||
|
|
||
|
refresh_input_resource_list()
|
||
|
{
|
||
|
var display_list = [];
|
||
|
for (const subresource_unique_name of this.pass_data['InputResources'])
|
||
|
{
|
||
|
var subresource_info = parse_subresource_unique_name(subresource_unique_name);
|
||
|
var resource_desc = get_resource_desc(subresource_info['resource']);
|
||
|
|
||
|
var name = prettify_subresource_unique_name(subresource_info, resource_desc);
|
||
|
var href = null;
|
||
|
if (resource_desc)
|
||
|
{
|
||
|
href = `#display_input_resource(${this.pass_id},'${subresource_unique_name}');`
|
||
|
}
|
||
|
|
||
|
display_list.push({'name': name, 'href': href});
|
||
|
}
|
||
|
|
||
|
render_selection_list_html(
|
||
|
document.getElementById('pass_input_resource_list'),
|
||
|
display_list,
|
||
|
{
|
||
|
'search': document.getElementById('pass_input_resource_search').value,
|
||
|
'deduplicate': true,
|
||
|
'sort': true
|
||
|
});
|
||
|
}
|
||
|
|
||
|
refresh_output_resource_list()
|
||
|
{
|
||
|
var draw_id = -1;
|
||
|
if (this.resource_view)
|
||
|
{
|
||
|
draw_id = this.resource_view.subresource_version_info['draw'];
|
||
|
}
|
||
|
|
||
|
var display_list = [];
|
||
|
for (const subresource_unique_name of this.pass_data['OutputResources'])
|
||
|
{
|
||
|
var subresource_info = parse_subresource_unique_name(subresource_unique_name);
|
||
|
var resource_desc = get_resource_desc(subresource_info['resource']);
|
||
|
|
||
|
var name = prettify_subresource_unique_name(subresource_info, resource_desc);
|
||
|
var href = null;
|
||
|
if (resource_desc)
|
||
|
{
|
||
|
if (draw_id >= 0)
|
||
|
{
|
||
|
href = `#display_draw_output_resource(${this.pass_id},'${subresource_unique_name}',${draw_id});`;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
href = `#display_output_resource(${this.pass_id},'${subresource_unique_name}');`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
display_list.push({'name': name, 'href': href});
|
||
|
}
|
||
|
|
||
|
render_selection_list_html(
|
||
|
document.getElementById('pass_output_resource_list'),
|
||
|
display_list,
|
||
|
{
|
||
|
'search': document.getElementById('pass_output_resource_search').value,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
refresh_draw_resource_list()
|
||
|
{
|
||
|
var is_output_resource = false;
|
||
|
var subresource_unique_name = '';
|
||
|
if (this.resource_view)
|
||
|
{
|
||
|
is_output_resource = this.pass_data['OutputResources'].includes(get_subresource_unique_name(this.resource_view.subresource_version_info));
|
||
|
subresource_unique_name = get_subresource_unique_name(this.resource_view.subresource_version_info);
|
||
|
}
|
||
|
|
||
|
var display_list = [];
|
||
|
for (var draw_id = 0; draw_id < this.pass_draws_data.length; draw_id++)
|
||
|
{
|
||
|
var href = null;
|
||
|
if (subresource_unique_name && is_output_resource)
|
||
|
{
|
||
|
href = `#display_draw_output_resource(${this.pass_id},'${subresource_unique_name}',${draw_id});`;
|
||
|
}
|
||
|
|
||
|
display_list.push({
|
||
|
'name': `${draw_id}: ${this.pass_draws_data[draw_id]['DrawName']}`,
|
||
|
'href': href
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (subresource_unique_name)
|
||
|
{
|
||
|
var href = null;
|
||
|
if (is_output_resource)
|
||
|
{
|
||
|
href = `#display_output_resource(${this.pass_id},'${subresource_unique_name}');`;
|
||
|
}
|
||
|
|
||
|
display_list.push({
|
||
|
'name': 'Final pass output',
|
||
|
'href': href
|
||
|
});
|
||
|
}
|
||
|
|
||
|
render_selection_list_html(
|
||
|
document.getElementById('pass_draw_list'),
|
||
|
display_list,
|
||
|
{
|
||
|
'search': document.getElementById('pass_draw_search').value,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
refresh_resource_modifying_list()
|
||
|
{
|
||
|
if (this.resource_view === null || this.resource_view.resource_desc['Desc'] == 'FRDGParameterStruct')
|
||
|
{
|
||
|
document.getElementById('resource_pass_modifying_outter').style.display = 'none';
|
||
|
return;
|
||
|
}
|
||
|
document.getElementById('resource_pass_modifying_outter').style.display = 'block';
|
||
|
|
||
|
var subresource_unique_name = get_subresource_unique_name(this.resource_view.subresource_version_info);
|
||
|
var resource_desc = get_resource_desc(this.resource_view.subresource_version_info['resource']);
|
||
|
|
||
|
var display_list = [];
|
||
|
for (var pass_id = 0; pass_id < g_passes.length; pass_id++)
|
||
|
{
|
||
|
var producer = false;
|
||
|
g_passes[pass_id]['OutputResources'].forEach(function(value) {
|
||
|
if (value == subresource_unique_name)
|
||
|
{
|
||
|
producer = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
if (producer)
|
||
|
{
|
||
|
display_list.push({
|
||
|
'name': g_passes[pass_id]['EventName'],
|
||
|
'href': `#display_output_resource(${pass_id},'${subresource_unique_name}');`,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
document.getElementById('resource_pass_modifying_title').innerHTML = `Passes modifying ${resource_desc['Name']}`;
|
||
|
render_selection_list_html(
|
||
|
document.getElementById('resource_pass_modifying_list'),
|
||
|
display_list,
|
||
|
{
|
||
|
'search': document.getElementById('resource_pass_modifying_search').value,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
refresh_resource_reading_list()
|
||
|
{
|
||
|
if (this.resource_view === null || this.resource_view.resource_desc['Desc'] == 'FRDGParameterStruct')
|
||
|
{
|
||
|
document.getElementById('resource_pass_reading_outter').style.display = 'none';
|
||
|
return;
|
||
|
}
|
||
|
document.getElementById('resource_pass_reading_outter').style.display = 'block';
|
||
|
|
||
|
var subresource_unique_name = get_subresource_unique_name(this.resource_view.subresource_version_info);
|
||
|
var resource_desc = get_resource_desc(this.resource_view.subresource_version_info['resource']);
|
||
|
|
||
|
var display_list = [];
|
||
|
for (var pass_id = 0; pass_id < g_passes.length; pass_id++)
|
||
|
{
|
||
|
var reader = false;
|
||
|
g_passes[pass_id]['InputResources'].forEach(function(value) {
|
||
|
if (value == subresource_unique_name)
|
||
|
{
|
||
|
reader = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
if (reader)
|
||
|
{
|
||
|
display_list.push({
|
||
|
'name': g_passes[pass_id]['EventName'],
|
||
|
'href': `#display_input_resource(${pass_id},'${subresource_unique_name}');`,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
document.getElementById('resource_pass_reading_title').innerHTML = `Passes reading ${resource_desc['Name']}`;
|
||
|
render_selection_list_html(
|
||
|
document.getElementById('resource_pass_reading_list'),
|
||
|
display_list,
|
||
|
{
|
||
|
'search': document.getElementById('resource_pass_reading_search').value,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
resize(ctx)
|
||
|
{
|
||
|
if (this.resource_view !== null)
|
||
|
{
|
||
|
this.resource_view.resize(ctx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
set_resource_view(new_resource_view)
|
||
|
{
|
||
|
var parent_dom = document.getElementById('display_resource_pannel');
|
||
|
if (this.resource_view !== null)
|
||
|
{
|
||
|
this.resource_view.release();
|
||
|
delete this.resource_view;
|
||
|
parent_dom.innerHTML = '';
|
||
|
}
|
||
|
|
||
|
this.resource_view = new_resource_view;
|
||
|
if (this.resource_view !== null)
|
||
|
{
|
||
|
this.resource_view.setup_html(parent_dom);
|
||
|
this.refresh_all_lists();
|
||
|
onresize_body();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
var navs = [`display_pass(${this.pass_id});`];
|
||
|
|
||
|
if (this.resource_view !== null)
|
||
|
{
|
||
|
navs.concat(this.resource_view.navigations);
|
||
|
}
|
||
|
|
||
|
return navs;
|
||
|
}
|
||
|
|
||
|
release()
|
||
|
{
|
||
|
this.set_resource_view(null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function display_pass_internal(pass_id)
|
||
|
{
|
||
|
if (g_view instanceof PassView && pass_id == g_view.pass_id)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_main_view(new PassView(pass_id));
|
||
|
}
|
||
|
|
||
|
function display_resource_internal(subresource_version_info)
|
||
|
{
|
||
|
var resource_desc = get_resource_desc(subresource_version_info['resource']);
|
||
|
if (!resource_desc)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (g_view.resource_view !== null && get_subresource_unique_version_name(g_view.resource_view.subresource_version_info) == get_subresource_unique_version_name(subresource_version_info))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (resource_desc['Desc'] == 'FRDGBufferDesc')
|
||
|
{
|
||
|
g_view.set_resource_view(new BufferView(subresource_version_info, resource_desc));
|
||
|
}
|
||
|
else if (resource_desc['Desc'] == 'FRDGTextureDesc' && (resource_desc['Type'] == 'Texture2D' || resource_desc['Type'] == 'Texture2DArray'))
|
||
|
{
|
||
|
var new_texture_view = new TextureView(subresource_version_info, resource_desc);
|
||
|
|
||
|
// Keep the same zoom settings if the subresources are exactly the same extent
|
||
|
if (g_view && 'resource_view' in g_view && g_view.resource_view instanceof TextureView)
|
||
|
{
|
||
|
var zoom_settings = g_view.resource_view.get_zoom_settings();
|
||
|
|
||
|
if (new_texture_view.is_zoom_settings_compatible(zoom_settings))
|
||
|
{
|
||
|
new_texture_view.onload = function() {
|
||
|
new_texture_view.apply_zoom_settings(zoom_settings);
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_view.set_resource_view(new_texture_view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- HREF FUNCTIONS
|
||
|
|
||
|
function display_pass(pass_id)
|
||
|
{
|
||
|
// Load first resource
|
||
|
if (g_passes[pass_id]['OutputResources'][0])
|
||
|
{
|
||
|
redirect_to_hash(`display_output_resource(${pass_id},'${g_passes[pass_id]['OutputResources'][0]}');`);
|
||
|
}
|
||
|
else if (g_passes[pass_id]['InputResources'][0])
|
||
|
{
|
||
|
redirect_to_hash(`display_input_resource(${pass_id},'${g_passes[pass_id]['InputResources'][0]}');`);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
display_pass_internal(pass_id);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function display_input_resource(pass_id, subresource_unique_name)
|
||
|
{
|
||
|
var previous_producer_pass = -1;
|
||
|
for (var i = 0; i < pass_id; i++)
|
||
|
{
|
||
|
var cur_outputs = g_passes[i]['OutputResources'];
|
||
|
|
||
|
cur_outputs.forEach(function(value) {
|
||
|
if (value == subresource_unique_name)
|
||
|
{
|
||
|
previous_producer_pass = i;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var subresource_version_info = parse_subresource_unique_name(subresource_unique_name);
|
||
|
if (previous_producer_pass >= 0)
|
||
|
{
|
||
|
subresource_version_info['pass'] = g_passes[previous_producer_pass]['Pointer'];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
subresource_version_info['pass'] = k_null_json_ptr;
|
||
|
}
|
||
|
|
||
|
display_pass_internal(pass_id);
|
||
|
display_resource_internal(subresource_version_info);
|
||
|
}
|
||
|
|
||
|
function display_output_resource(pass_id, subresource_unique_name)
|
||
|
{
|
||
|
var subresource_version_info = parse_subresource_unique_name(subresource_unique_name);
|
||
|
subresource_version_info['pass'] = g_passes[pass_id]['Pointer'];
|
||
|
|
||
|
display_pass_internal(pass_id);
|
||
|
display_resource_internal(subresource_version_info);
|
||
|
}
|
||
|
|
||
|
function display_draw_output_resource(pass_id, subresource_unique_name, draw_id)
|
||
|
{
|
||
|
var subresource_version_info = parse_subresource_unique_name(subresource_unique_name);
|
||
|
subresource_version_info['pass'] = g_passes[pass_id]['Pointer'];
|
||
|
subresource_version_info['draw'] = draw_id;
|
||
|
|
||
|
display_pass_internal(pass_id);
|
||
|
display_resource_internal(subresource_version_info);
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY VIEWER CONSOLE
|
||
|
|
||
|
var g_console_events = [];
|
||
|
var g_console_error_count = 0;
|
||
|
|
||
|
class ConsoleEvent
|
||
|
{
|
||
|
constructor(type, message)
|
||
|
{
|
||
|
this.type = type;
|
||
|
this.message = message;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class ConsoleView extends IView
|
||
|
{
|
||
|
constructor()
|
||
|
{
|
||
|
super();
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
parent_dom.innerHTML = `
|
||
|
<div class="main_div">
|
||
|
<div class="pass_title">Viewer Console</div>
|
||
|
<div id="console_events_pannel"></div>
|
||
|
</div>`;
|
||
|
|
||
|
document.title = 'Viewer Console';
|
||
|
this.update_console_events();
|
||
|
}
|
||
|
|
||
|
update_console_events()
|
||
|
{
|
||
|
var html = `
|
||
|
<table width="100%" class="pretty_table">`;
|
||
|
|
||
|
for (var console_event of g_console_events)
|
||
|
{
|
||
|
html += `
|
||
|
<tr class="${console_event.type}">
|
||
|
<td>${console_event.type}: ${console_event.message}</td>
|
||
|
</tr>`;
|
||
|
};
|
||
|
|
||
|
html += `
|
||
|
</table>`;
|
||
|
|
||
|
document.getElementById('console_events_pannel').innerHTML = html;
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
return [`display_console();`];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function update_console_button()
|
||
|
{
|
||
|
if (document.getElementById('console_button') && g_console_error_count > 0)
|
||
|
{
|
||
|
document.getElementById('console_button').classList.add('error');
|
||
|
document.getElementById('console_button').innerHTML = `Console (${g_console_error_count} Errors)`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function add_console_event(type, message)
|
||
|
{
|
||
|
console.assert(['error', 'log'].includes(type));
|
||
|
g_console_events.push(new ConsoleEvent(type, message));
|
||
|
|
||
|
if (type == 'error')
|
||
|
{
|
||
|
g_console_error_count += 1;
|
||
|
update_console_button();
|
||
|
}
|
||
|
|
||
|
if (g_view instanceof ConsoleView)
|
||
|
{
|
||
|
g_view.update_console_events();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function display_console(tip_id)
|
||
|
{
|
||
|
set_main_view(new ConsoleView());
|
||
|
}
|
||
|
|
||
|
function init_console()
|
||
|
{
|
||
|
window.addEventListener('error', function(event) {
|
||
|
add_console_event('error', `${event.filename}:${event.fileno}: ${event.message}`);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY INFOS
|
||
|
|
||
|
class InfosView extends IView
|
||
|
{
|
||
|
constructor()
|
||
|
{
|
||
|
super();
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
var info_htmls = '';
|
||
|
{
|
||
|
info_htmls += `
|
||
|
<table width="100%" class="pretty_table">`;
|
||
|
|
||
|
for (var key in g_infos)
|
||
|
{
|
||
|
if (g_infos.hasOwnProperty(key))
|
||
|
{
|
||
|
info_htmls += `
|
||
|
<tr>
|
||
|
<td width="100px">${key}</td>
|
||
|
<td>${g_infos[key]}</td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
info_htmls += `
|
||
|
</table>`;
|
||
|
}
|
||
|
|
||
|
parent_dom.innerHTML = `
|
||
|
<div class="main_div">
|
||
|
<div class="pass_title">Infos</div>
|
||
|
<div id="infos_pannel">${info_htmls}</div>
|
||
|
</div>`;
|
||
|
|
||
|
if (does_file_exists('Base/Screenshot.png'))
|
||
|
{
|
||
|
parent_dom.innerHTML += `
|
||
|
<div class="main_div">
|
||
|
<div class="pass_title">Screenshot</div>
|
||
|
<img src="${k_current_dir}/Base/Screenshot.png" style="width: 100%;" />
|
||
|
</div>`;
|
||
|
}
|
||
|
|
||
|
document.title = 'Dump infos';
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
return [`display_infos();`];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function display_infos(tip_id)
|
||
|
{
|
||
|
set_main_view(new InfosView());
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY TIP
|
||
|
|
||
|
const k_tips = [
|
||
|
// Dumping process
|
||
|
'Can use the CTRL+SHIFT+/ keyboard shortcut to summon the DumpGPU command.',
|
||
|
'Speed up your frame dump by only selecting the passes you need with r.DumpGPU.Root. For instance r.DumpGPU.Root="*PostProcessing*".',
|
||
|
'GPU dumps can be large and accumulate on your hard drive in your various projects\' Saved/ directories. Set r.DumpGPU.Directory="D:/tmp/DumpGPU/" in your console variables or UE-DumpGPUPath environment variable to dump them all at the same location on your machine.',
|
||
|
'Uses -cvarsini to override console variables with your own ConsoleVariables.ini file on a cooked build.',
|
||
|
|
||
|
// General navigations
|
||
|
'All navigation links can be open in a new tab.',
|
||
|
'Uses the browser\'s back button to navigate to previously inspected resource.',
|
||
|
'Make sure to browse the dump informations.',
|
||
|
'Make sure to browse the console variables.',
|
||
|
'Make sure to browse the log file.',
|
||
|
'Share the part of the URL after the # (for instance #display_input_resource(96,\'TSR.AntiAliasing.Noise.000000006b9b2c00.mip0\');) to anyone else whom have this GPU dump to so they too can navigate to the exact same resource view.',
|
||
|
'Set r.DumpGPU.Viewer.Visualize in your ConsoleVariables.ini so the dump viewer automatically open this RDG output resource at startup.',
|
||
|
|
||
|
// Buffer visualization
|
||
|
'Uses the templated FRDGBufferDesc::Create*<FMyStructure>(NumElements) to display your buffer more conveniently with FMyStructure layout in the buffer visualization.',
|
||
|
'Buffer visualization supports float, half, int, uint, short, ushort, char, uchar, as well as hexadecimal with hex(uint) and binary with bin(uint).',
|
||
|
'Uses the "Address..." field at the very left of the buffer view\'s header to navigate to a specific address. Supports decimal and 0x prefixed hexadecimal notations.',
|
||
|
|
||
|
// Texture visualization
|
||
|
'Click the texture viewer to then zoom in and out with your mouse wheel.',
|
||
|
'Right click to drag texture viewport arround when zoomed in.',
|
||
|
'Uses the texture viewer\'s "copy to clipboard" to share your texture visualization to anyone.',
|
||
|
];
|
||
|
|
||
|
class TipView extends IView
|
||
|
{
|
||
|
constructor(tip_id)
|
||
|
{
|
||
|
super();
|
||
|
this.tip_id = tip_id;
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
parent_dom.innerHTML = `
|
||
|
<div style="padding-top: 20%; width: 50%; margin: 0 auto; font-size: 14;">
|
||
|
<div>User tip:</div>
|
||
|
<div style="padding: 20px;">${k_tips[this.tip_id]}</div>
|
||
|
<div class="button_list" style="margin-left: auto; display: block; width: 100px;">
|
||
|
<a href="#display_tip(${(this.tip_id + 1) % k_tips.length});">Next</a>
|
||
|
</div>
|
||
|
</div>`;
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
return [`display_tip(${this.tip_id});`];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function display_tip(tip_id)
|
||
|
{
|
||
|
if (tip_id === undefined)
|
||
|
{
|
||
|
tip_id = Math.floor(Math.random() * k_tips.length);
|
||
|
document.title = 'GPU Dump Viewer';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.title = `User tip #${tip_id + 1}`;
|
||
|
}
|
||
|
|
||
|
set_main_view(new TipView(tip_id));
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY CONSOLE VARIABLES
|
||
|
|
||
|
class CVarsView extends IView
|
||
|
{
|
||
|
constructor()
|
||
|
{
|
||
|
super();
|
||
|
this.load_cvars();
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
parent_dom.innerHTML = `
|
||
|
<div class="main_div">
|
||
|
<div class="pass_title">Console variables</div>
|
||
|
<div class="selection_list_search">
|
||
|
<input type="search" id="cvars_search_input" oninput="g_view.refresh_cvars();" onchange="g_view.refresh_cvars();" style="width: 100%;" placeholder="Search console variables..." />
|
||
|
</div>
|
||
|
<div id="cvars_pannel"></div>
|
||
|
</div>`;
|
||
|
|
||
|
this.refresh_cvars();
|
||
|
document.title = 'Console variables';
|
||
|
}
|
||
|
|
||
|
load_cvars()
|
||
|
{
|
||
|
var cvars_csv = load_text_file('Base/ConsoleVariables.csv');
|
||
|
|
||
|
this.cvars = [];
|
||
|
var cvars_set = new Set();
|
||
|
var csv_lines = cvars_csv.split('\n');
|
||
|
for (var i = 1; i < csv_lines.length - 1; i++)
|
||
|
{
|
||
|
var csv_line = csv_lines[i].split(',');
|
||
|
var entry = {};
|
||
|
entry['name'] = csv_line[0];
|
||
|
entry['type'] = csv_line[1];
|
||
|
entry['set_by'] = csv_line[2];
|
||
|
entry['value'] = csv_line[3];
|
||
|
|
||
|
if (!cvars_set.has(entry['name']))
|
||
|
{
|
||
|
cvars_set.add(entry['name']);
|
||
|
this.cvars.push(entry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.cvars.sort(function(a, b)
|
||
|
{
|
||
|
if (a['name'] < b['name'])
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else if (a['name'] > b['name'])
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
refresh_cvars()
|
||
|
{
|
||
|
var html = `
|
||
|
<table width="100%" class="pretty_table">
|
||
|
<tr class="header">
|
||
|
<td>Name</td>
|
||
|
<td width="15%">Value</td>
|
||
|
<td width="30px">Type</td>
|
||
|
<td width="100px">Set by</td>
|
||
|
</tr>`;
|
||
|
|
||
|
var cvar_search = document.getElementById('cvars_search_input').value.toLowerCase();
|
||
|
|
||
|
this.cvars.forEach(function(cvar)
|
||
|
{
|
||
|
var display = true;
|
||
|
if (cvar_search)
|
||
|
{
|
||
|
display = cvar['name'].toLowerCase().includes(cvar_search);
|
||
|
}
|
||
|
|
||
|
if (display)
|
||
|
{
|
||
|
html += `
|
||
|
<tr>
|
||
|
<td>${cvar['name']}</td>
|
||
|
<td>${cvar['value']}</td>
|
||
|
<td>${cvar['type']}</td>
|
||
|
<td>${cvar['set_by']}</td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
html += `
|
||
|
</table>`;
|
||
|
|
||
|
document.getElementById('cvars_pannel').innerHTML = html;
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
return [`display_cvars();`];
|
||
|
}
|
||
|
|
||
|
release()
|
||
|
{
|
||
|
this.cvars = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function display_cvars()
|
||
|
{
|
||
|
set_main_view(new CVarsView());
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY LOG
|
||
|
|
||
|
function get_log_path()
|
||
|
{
|
||
|
return `Base/${g_infos['Project']}.log`;
|
||
|
}
|
||
|
|
||
|
class LogView extends IView
|
||
|
{
|
||
|
constructor()
|
||
|
{
|
||
|
super();
|
||
|
|
||
|
this.log = [];
|
||
|
|
||
|
var log = load_text_file(get_log_path());
|
||
|
if (log)
|
||
|
{
|
||
|
this.log = log.split('\n');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
parent_dom.innerHTML = `
|
||
|
<div class="main_div">
|
||
|
<div class="pass_title">Log</div>
|
||
|
<div class="selection_list_search">
|
||
|
<input type="search" id="log_search_input" oninput="g_view.refresh_log();" onchange="g_view.refresh_log();" style="width: 100%;" placeholder="Search log..." />
|
||
|
</div>
|
||
|
<div id="log_pannel"></div>
|
||
|
</div>`;
|
||
|
|
||
|
this.refresh_log();
|
||
|
document.title = 'Log';
|
||
|
}
|
||
|
|
||
|
refresh_log()
|
||
|
{
|
||
|
var html = `
|
||
|
<table width="100%" class="pretty_table">`;
|
||
|
|
||
|
var log_search = document.getElementById('log_search_input').value.toLowerCase();
|
||
|
|
||
|
this.log.forEach(function(log_line)
|
||
|
{
|
||
|
var display = true;
|
||
|
if (log_search)
|
||
|
{
|
||
|
display = log_line.toLowerCase().includes(log_search);
|
||
|
}
|
||
|
|
||
|
if (display)
|
||
|
{
|
||
|
html += `
|
||
|
<tr>
|
||
|
<td>${log_line}</td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
html += `
|
||
|
</table>`;
|
||
|
|
||
|
document.getElementById('log_pannel').innerHTML = html;
|
||
|
}
|
||
|
|
||
|
get navigations()
|
||
|
{
|
||
|
return [`display_log();`];
|
||
|
}
|
||
|
|
||
|
release()
|
||
|
{
|
||
|
this.log = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function display_log()
|
||
|
{
|
||
|
set_main_view(new LogView());
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY TEXTURE
|
||
|
|
||
|
var k_exotic_raw_channel_bit_depth = 0;
|
||
|
var g_shader_code_dict = {};
|
||
|
|
||
|
class TextureView extends ResourceView
|
||
|
{
|
||
|
constructor(subresource_version_info, resource_desc)
|
||
|
{
|
||
|
super(subresource_version_info, resource_desc);
|
||
|
this.is_ready = false;
|
||
|
this.raw_texture_data = null;
|
||
|
this.subresource_desc = null;
|
||
|
this.release_gl();
|
||
|
this.viewport_width = 1;
|
||
|
this.pixel_scaling = 0;
|
||
|
this.viewport_selected = false;
|
||
|
this.is_draging_canvas = false;
|
||
|
this.display_mode = 'Visualization';
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
|
||
|
var dpi = window.devicePixelRatio || 1;
|
||
|
|
||
|
var canvas_rendering_res_x = subresource_extent['x'];
|
||
|
var canvas_rendering_res_y = subresource_extent['y'];
|
||
|
|
||
|
var canvas_display_w = canvas_rendering_res_x / dpi;
|
||
|
var canvas_display_h = canvas_rendering_res_y / dpi;
|
||
|
|
||
|
var resource_info_htmls = '';
|
||
|
{
|
||
|
resource_info_htmls += `
|
||
|
<table width="100%" class="pretty_table resource_desc">`;
|
||
|
|
||
|
for (var key in this.resource_desc)
|
||
|
{
|
||
|
if (this.resource_desc.hasOwnProperty(key)){
|
||
|
resource_info_htmls += `
|
||
|
<tr>
|
||
|
<td>${key}</td>
|
||
|
<td>${this.resource_desc[key]}</td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resource_info_htmls += `
|
||
|
</table>`;
|
||
|
}
|
||
|
|
||
|
var title = prettify_subresource_unique_name(this.subresource_version_info, this.resource_desc);
|
||
|
if (this.subresource_version_info['draw'] >= 0)
|
||
|
{
|
||
|
title += ` draw:${this.subresource_version_info['draw']}`;
|
||
|
}
|
||
|
|
||
|
var html = `
|
||
|
<div class="main_div">
|
||
|
<div class="selection_list_title">Texture visualization: ${title}</div>
|
||
|
<div id="canvas_outter" style="margin-bottom: 10px;">
|
||
|
<div id="canvas_header">
|
||
|
<div class="button_list" id="texture_visualization_change_pixel_scaling"><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_pixel_scaling(this);" value="Fit"/><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_pixel_scaling(this);" value="1:1"/><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_pixel_scaling(this);" value="2:1"/><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_pixel_scaling(this);" value="4:1"/><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_pixel_scaling(this);" value="8:1"/><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_pixel_scaling(this);" value="16:1"/>
|
||
|
</div>
|
||
|
<div class="button_list">
|
||
|
<input type="button" onclick="g_view.resource_view.copy_canvas_to_clipboard();" value="Copy to clipboard"/>
|
||
|
</div>
|
||
|
<div class="button_list" id="texture_visualization_change_display_modes"><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_display_mode(this);" value="Visualization"/><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_display_mode(this);" value="NaN"/><!---
|
||
|
---><input type="button" onclick="g_view.resource_view.change_display_mode(this);" value="Inf"/>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div id="canvas_viewport">
|
||
|
<canvas
|
||
|
id="texture_visualization_canvas"
|
||
|
width="${canvas_rendering_res_x}"
|
||
|
height="${canvas_rendering_res_y}"
|
||
|
style="width: ${canvas_display_w}px; height: ${canvas_display_h}px;"></canvas>
|
||
|
</div>
|
||
|
<div id="canvas_footer">
|
||
|
<table>
|
||
|
<tr>
|
||
|
<td>Cursor texel pos:</td>
|
||
|
<td id="canvas_hover_texel_info"></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Selected texel pos:</td>
|
||
|
<td id="canvas_selected_texel_info"></td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
</div>
|
||
|
</div>
|
||
|
<table width="100%">
|
||
|
<tr>
|
||
|
<td width="50%">
|
||
|
<div class="selection_list_title">Texture descriptor</div>
|
||
|
${resource_info_htmls}
|
||
|
</td>
|
||
|
<td style="width: 50%;">
|
||
|
<textarea id="texture_visualization_code_input" oninput="g_view.resource_view.shader_user_code_change();" onchange="g_view.resource_view.shader_user_code_change();"></textarea>
|
||
|
<textarea id="texture_visualization_code_log" readonly></textarea>
|
||
|
<div style="padding: 3px 10px;"><a class="external_link" href="https://www.khronos.org/files/webgl20-reference-guide.pdf">WebGL 2.0's quick reference card</a></div>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
</div>`;
|
||
|
|
||
|
parent_dom.innerHTML = html;
|
||
|
|
||
|
update_value_selection(document.getElementById('texture_visualization_change_pixel_scaling'), `Fit`);
|
||
|
update_value_selection(document.getElementById('texture_visualization_change_display_modes'), `Visualization`);
|
||
|
|
||
|
// Init WebGL 2.0
|
||
|
this.init_gl();
|
||
|
|
||
|
// Translate the descriptor of the subresource.
|
||
|
this.subresource_desc = this.translate_subresource_desc();
|
||
|
if (this.subresource_desc === null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Setup mouse motion callback
|
||
|
{
|
||
|
var texture_view = this;
|
||
|
|
||
|
this.canvas.onclick = function(event) { texture_view.canvas_click(event); }
|
||
|
this.canvas.onmouseenter = function(event) { texture_view.canvas_onmousemove(event); };
|
||
|
this.canvas.onmousemove = function(event) { texture_view.canvas_onmousemove(event); };
|
||
|
this.canvas.onmousedown = function(event) { texture_view.canvas_onmousedown(event); };
|
||
|
this.canvas.onmouseup = function(event) { texture_view.canvas_onmouseup(event); };
|
||
|
this.canvas.oncontextmenu = function(event) { return false; };
|
||
|
|
||
|
document.getElementById('canvas_viewport').oncontextmenu = function(event) { return false; };
|
||
|
|
||
|
document.getElementById('canvas_outter').onclick = function(event) { texture_view.onclick_canvas_outter(); };
|
||
|
document.getElementById('canvas_outter').onmouseleave = function(event) { texture_view.set_select_viewport(false); };
|
||
|
}
|
||
|
|
||
|
if (this.subresource_desc['raw_channel_format'] == 'float')
|
||
|
{
|
||
|
update_value_selection(document.getElementById('texture_visualization_change_display_modes'), this.display_mode);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.getElementById('texture_visualization_change_display_modes').style.display = 'none';
|
||
|
}
|
||
|
|
||
|
// Fill out the default visualization shader code in the textarea for the user to customise.
|
||
|
{
|
||
|
var user_shader_code_dom = document.getElementById('texture_visualization_code_input');
|
||
|
|
||
|
if (this.shader_code_saving_key in g_shader_code_dict)
|
||
|
{
|
||
|
user_shader_code_dom.value = g_shader_code_dict[this.shader_code_saving_key];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!this.subresource_desc)
|
||
|
{
|
||
|
user_shader_code_dom.value = `// Sorry but ${this.resource_desc['Format']} is not supported for display :(`;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
user_shader_code_dom.value = this.subresource_desc['webgl_pixel_shader_public'];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var texture_view = this;
|
||
|
var subresource_version_name = get_subresource_unique_version_name(this.subresource_version_info);
|
||
|
load_resource_binary_file(`Resources/${subresource_version_name}.bin`, function(raw_texture_data)
|
||
|
{
|
||
|
if (raw_texture_data)
|
||
|
{
|
||
|
texture_view.is_ready = true;
|
||
|
texture_view.raw_texture_data = raw_texture_data;
|
||
|
texture_view.resize_canvas(texture_view.pixel_scaling);
|
||
|
texture_view.process_texture_data_for_visualization();
|
||
|
texture_view.refresh_texture_visualization();
|
||
|
texture_view.onload();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
texture_view.error(`Error: Resources/${subresource_version_name}.bin couldn't be loaded.`);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
document.title = `${g_view.pass_data['EventName']} - ${this.resource_desc['Name']} ${this.subresource_version_info['subresource']}`;
|
||
|
}
|
||
|
|
||
|
error(error_msg)
|
||
|
{
|
||
|
console.error(error_msg);
|
||
|
document.getElementById('canvas_viewport').innerHTML = `
|
||
|
<div class="error_msg">
|
||
|
${error_msg}
|
||
|
</div>`;
|
||
|
document.getElementById('texture_visualization_code_input').readOnly = true;
|
||
|
}
|
||
|
|
||
|
image_load_uint(texel_x, texel_y)
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
|
||
|
var extent = this.get_subresource_extent();
|
||
|
var pixel_data_offset = texel_x + (extent['y'] - 1 - texel_y) * extent['x'];
|
||
|
|
||
|
var texel_value = [0];
|
||
|
for (var c = 1; c < this.subresource_desc['raw_channel_count']; c++)
|
||
|
{
|
||
|
texel_value.push(0);
|
||
|
}
|
||
|
|
||
|
if (this.subresource_desc['webgl_src_type'] === this.gl.UNSIGNED_SHORT_5_6_5)
|
||
|
{
|
||
|
var data = new Uint16Array(this.raw_texture_data, pixel_data_offset * 2, 1);
|
||
|
|
||
|
texel_value[0] = (data[0] >> 0) & 0x1F;
|
||
|
texel_value[1] = (data[0] >> 5) & 0x3F;
|
||
|
texel_value[2] = (data[0] >> 11) & 0x1F;
|
||
|
}
|
||
|
else if (this.subresource_desc['webgl_src_type'] === this.gl.UNSIGNED_INT_10F_11F_11F_REV)
|
||
|
{
|
||
|
var data = new Uint32Array(this.raw_texture_data, pixel_data_offset * 4, 1);
|
||
|
|
||
|
texel_value[0] = (data[0] >> 0) & 0x7FF;
|
||
|
texel_value[1] = (data[0] >> 11) & 0x7FF;
|
||
|
texel_value[2] = (data[0] >> 22) & 0x3FF;
|
||
|
}
|
||
|
else if (this.subresource_desc['webgl_src_type'] === this.gl.UNSIGNED_INT_2_10_10_10_REV)
|
||
|
{
|
||
|
var data = new Uint32Array(this.raw_texture_data, pixel_data_offset * 4, 1);
|
||
|
|
||
|
texel_value[0] = (data[0] >> 0) & 0x3FF;
|
||
|
texel_value[1] = (data[0] >> 10) & 0x3FF;
|
||
|
texel_value[2] = (data[0] >> 20) & 0x3FF;
|
||
|
texel_value[3] = (data[0] >> 30) & 0x3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var data = null;
|
||
|
if (this.subresource_desc['raw_channel_bit_depth'] === 8)
|
||
|
{
|
||
|
data = new Uint8Array(this.raw_texture_data, this.subresource_desc['raw_channel_count'] * pixel_data_offset * 1, this.subresource_desc['raw_channel_count']);
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_bit_depth'] === 16)
|
||
|
{
|
||
|
data = new Uint16Array(this.raw_texture_data, this.subresource_desc['raw_channel_count'] * pixel_data_offset * 2, this.subresource_desc['raw_channel_count']);
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_bit_depth'] === 32)
|
||
|
{
|
||
|
data = new Uint32Array(this.raw_texture_data, this.subresource_desc['raw_channel_count'] * pixel_data_offset * 4, this.subresource_desc['raw_channel_count']);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (var c = 0; c < this.subresource_desc['raw_channel_count']; c++)
|
||
|
{
|
||
|
texel_value[c] = data[this.subresource_desc['raw_channel_swizzling'][c]];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return texel_value;
|
||
|
}
|
||
|
|
||
|
decode_texel_uint(value)
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
|
||
|
value = [...value];
|
||
|
|
||
|
if (this.subresource_desc['ue_pixel_format'] === 'PF_FloatRGB' || this.subresource_desc['ue_pixel_format'] === 'PF_FloatR11G11B10')
|
||
|
{
|
||
|
value[0] = decode_float11(value[0]);
|
||
|
value[1] = decode_float11(value[1]);
|
||
|
value[2] = decode_float10(value[2]);
|
||
|
}
|
||
|
else if (this.subresource_desc['ue_pixel_format'] === 'PF_A2B10G10R10')
|
||
|
{
|
||
|
value[0] = value[0] / 1023.0;
|
||
|
value[1] = value[1] / 1023.0;
|
||
|
value[2] = value[2] / 1023.0;
|
||
|
value[3] = value[3] / 3.0;
|
||
|
}
|
||
|
else if (this.subresource_desc['ue_pixel_format'] === 'PF_R5G6B5_UNORM')
|
||
|
{
|
||
|
value[0] = value[0] / 31.0;
|
||
|
value[1] = value[1] / 63.0;
|
||
|
value[2] = value[2] / 31.0;
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_format'] == 'uint')
|
||
|
{
|
||
|
// NOP
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_format'] == 'float')
|
||
|
{
|
||
|
for (var c = 0; c < value.length; c++)
|
||
|
{
|
||
|
if (this.subresource_desc['raw_channel_bit_depth'] == 16)
|
||
|
{
|
||
|
value[c] = decode_float16(value[c]);
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_bit_depth'] == 32)
|
||
|
{
|
||
|
value[c] = decode_float32(value[c]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
console.error('Unknown float bit depth');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_format'] == 'unorm')
|
||
|
{
|
||
|
var divide = Number((BigInt(1) << BigInt(this.subresource_desc['raw_channel_bit_depth'])) - 1n);
|
||
|
|
||
|
for (var c = 0; c < value.length; c++)
|
||
|
{
|
||
|
value[c] = Number(value[c]) / divide;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
console.error('Unknown pixel format to convert');
|
||
|
}
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
update_texel_cursor_pos(texel_x, texel_y, parent_dom_name)
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
|
||
|
var parent_dom = document.getElementById(parent_dom_name);
|
||
|
parent_dom.innerHTML = `<span style="width: 40px; display: inline-block; text-align: right;">${texel_x}</span> <span style="width: 40px; display: inline-block; text-align: right; margin-right: 60px;">${texel_y}</span> `;
|
||
|
|
||
|
// Find out pixel value.
|
||
|
{
|
||
|
var texel_raw = this.image_load_uint(texel_x, texel_y);
|
||
|
var texel_value = this.decode_texel_uint(texel_raw);
|
||
|
|
||
|
const channel_name = ['R', 'G', 'B', 'A'];
|
||
|
const channel_color = ['rgb(255, 38, 38)', 'rgb(38, 255, 38)', 'rgb(38, 187, 255)', 'white'];
|
||
|
|
||
|
var texel_string = [];
|
||
|
for (var c = 0; c < this.subresource_desc['raw_channel_count']; c++)
|
||
|
{
|
||
|
texel_string.push(`${texel_value[c].toString()} (0x${texel_raw[c].toString(16)})`);
|
||
|
}
|
||
|
|
||
|
if (this.subresource_version_info['subresource'] == 'stencil')
|
||
|
{
|
||
|
texel_string[0] = '0b' + texel_value[0].toString(2).padStart(8, 0);
|
||
|
}
|
||
|
|
||
|
var html = ``;
|
||
|
for (var c = 0; c < this.subresource_desc['raw_channel_count']; c++)
|
||
|
{
|
||
|
html += `<span style="margin-left: 20px; color: ${channel_color[c]};">${channel_name[c]}: <span style="min-width: 170px; display: inline-block; text-align: right;">${texel_string[c]}</span></span>`;
|
||
|
}
|
||
|
|
||
|
parent_dom.innerHTML += html;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get_texel_coordinate_of_mouse(event)
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
|
||
|
var rect = event.target.getBoundingClientRect();
|
||
|
var html_x = event.clientX - rect.left;
|
||
|
var html_y = event.clientY - rect.top;
|
||
|
|
||
|
var texel_x = Math.floor(html_x * px_string_to_int(this.canvas.width) / px_string_to_int(this.canvas.style.width));
|
||
|
var texel_y = Math.floor(html_y * px_string_to_int(this.canvas.height) / px_string_to_int(this.canvas.style.height));
|
||
|
|
||
|
return [texel_x, texel_y];
|
||
|
}
|
||
|
|
||
|
onclick_canvas_outter()
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.set_select_viewport(true);
|
||
|
}
|
||
|
|
||
|
onmousewheel(event)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this.viewport_selected == false)
|
||
|
{
|
||
|
// forward scroll event to parent
|
||
|
document.getElementById('main_right_pannel').scrollTop += event.deltaY;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
var dpi = window.devicePixelRatio || 1;
|
||
|
var zooming_in = event.deltaY < 0.0;
|
||
|
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
|
||
|
// Find new pixel scaling to scale the viewport to.
|
||
|
var new_pixel_scaling = 0;
|
||
|
{
|
||
|
var viewport_width = (px_string_to_int(document.getElementById('canvas_viewport').style.width) - k_scroll_width) * dpi;
|
||
|
|
||
|
var min_pixel_scaling = Math.ceil(viewport_width / subresource_extent['x']);
|
||
|
if (min_pixel_scaling <= 1)
|
||
|
{
|
||
|
min_pixel_scaling = 1;
|
||
|
}
|
||
|
else if (min_pixel_scaling <= 2)
|
||
|
{
|
||
|
min_pixel_scaling = 2;
|
||
|
}
|
||
|
else if (min_pixel_scaling <= 4)
|
||
|
{
|
||
|
min_pixel_scaling = 4;
|
||
|
}
|
||
|
else if (min_pixel_scaling <= 8)
|
||
|
{
|
||
|
min_pixel_scaling = 8;
|
||
|
}
|
||
|
else if (min_pixel_scaling <= 16)
|
||
|
{
|
||
|
min_pixel_scaling = 16;
|
||
|
}
|
||
|
|
||
|
if (zooming_in)
|
||
|
{
|
||
|
if (this.pixel_scaling == 0)
|
||
|
{
|
||
|
if (min_pixel_scaling <= 16)
|
||
|
{
|
||
|
new_pixel_scaling = min_pixel_scaling;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
new_pixel_scaling = this.pixel_scaling * 2;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (this.pixel_scaling <= min_pixel_scaling)
|
||
|
{
|
||
|
new_pixel_scaling = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
new_pixel_scaling = this.pixel_scaling / 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
new_pixel_scaling = Math.min(Math.max(new_pixel_scaling, 0), 16);
|
||
|
}
|
||
|
|
||
|
var [texel_x, texel_y] = this.get_texel_coordinate_of_mouse(event);
|
||
|
|
||
|
this.resize_canvas(new_pixel_scaling, texel_x, texel_y);
|
||
|
this.update_change_pixel_scaling_button();
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
update_change_pixel_scaling_button()
|
||
|
{
|
||
|
const pixel_scaling_names = {
|
||
|
0: 'Fit',
|
||
|
1: '1:1',
|
||
|
2: '2:1',
|
||
|
4: '4:1',
|
||
|
8: '8:1',
|
||
|
16: '16:1',
|
||
|
};
|
||
|
update_value_selection(document.getElementById('texture_visualization_change_pixel_scaling'), pixel_scaling_names[this.pixel_scaling]);
|
||
|
}
|
||
|
|
||
|
canvas_click(event)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
|
||
|
var [texel_x, texel_y] = this.get_texel_coordinate_of_mouse(event);
|
||
|
|
||
|
texel_x = Math.min(Math.max(texel_x, 0), subresource_extent['x'] - 1);
|
||
|
texel_y = Math.min(Math.max(texel_y, 0), subresource_extent['y'] - 1);
|
||
|
|
||
|
this.update_texel_cursor_pos(texel_x, texel_y, 'canvas_selected_texel_info');
|
||
|
}
|
||
|
|
||
|
canvas_onmousemove(event)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this.is_draging_canvas)
|
||
|
{
|
||
|
this.drag_canvas(event);
|
||
|
}
|
||
|
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
|
||
|
var [texel_x, texel_y] = this.get_texel_coordinate_of_mouse(event);
|
||
|
|
||
|
texel_x = Math.min(Math.max(texel_x, 0), subresource_extent['x'] - 1);
|
||
|
texel_y = Math.min(Math.max(texel_y, 0), subresource_extent['y'] - 1);
|
||
|
|
||
|
this.update_texel_cursor_pos(texel_x, texel_y, 'canvas_hover_texel_info');
|
||
|
}
|
||
|
|
||
|
canvas_onmousedown(event)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (event.button == 2)
|
||
|
{
|
||
|
this.set_select_viewport(true);
|
||
|
this.is_draging_canvas = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
canvas_onmouseup(event)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (event.button == 2)
|
||
|
{
|
||
|
this.is_draging_canvas = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
drag_canvas(event)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var canvas_viewport = document.getElementById('canvas_viewport');
|
||
|
canvas_viewport.scrollLeft -= event.movementX;
|
||
|
canvas_viewport.scrollTop -= event.movementY;
|
||
|
}
|
||
|
|
||
|
set_select_viewport(selected)
|
||
|
{
|
||
|
if (this.viewport_selected === selected)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.viewport_selected = selected;
|
||
|
if (this.viewport_selected)
|
||
|
{
|
||
|
var texture_view = this;
|
||
|
document.getElementById('main_right_pannel').onwheel = function(event) { return false; };
|
||
|
document.getElementById('canvas_outter').classList.add('selected');
|
||
|
document.getElementById('canvas_viewport').style.overflow = 'scroll';
|
||
|
this.canvas.onwheel = function(event) { return texture_view.onmousewheel(event); };
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.getElementById('main_right_pannel').onwheel = null;
|
||
|
document.getElementById('canvas_outter').classList.remove('selected');
|
||
|
document.getElementById('canvas_viewport').style.overflow = 'hidden';
|
||
|
this.canvas.onwheel = null;
|
||
|
this.is_draging_canvas = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resize(ctx)
|
||
|
{
|
||
|
var dpi = window.devicePixelRatio || 1;
|
||
|
var viewport_width = ctx.width - 100;
|
||
|
var viewport_width_no_scroll = viewport_width - k_scroll_width;
|
||
|
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
var canvas_rendering_res_x = subresource_extent['x'];
|
||
|
var canvas_rendering_res_y = subresource_extent['y'];
|
||
|
|
||
|
var canvas_outter = document.getElementById('canvas_outter');
|
||
|
var canvas_viewport = document.getElementById('canvas_viewport');
|
||
|
canvas_outter.style.width = `${viewport_width}px`;
|
||
|
canvas_viewport.style.width = `${viewport_width_no_scroll + k_scroll_width}px`;
|
||
|
canvas_viewport.style.height = `${viewport_width_no_scroll * canvas_rendering_res_y / canvas_rendering_res_x + k_scroll_width}px`;
|
||
|
|
||
|
this.viewport_width = viewport_width;
|
||
|
if (this.is_ready)
|
||
|
{
|
||
|
this.resize_canvas(this.pixel_scaling);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resize_canvas(new_pixel_scaling, fix_texel_x, fix_texel_y)
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
var dpi = window.devicePixelRatio || 1;
|
||
|
var viewport_width = this.viewport_width;
|
||
|
var viewport_width_no_scroll = viewport_width - k_scroll_width;
|
||
|
|
||
|
var canvas_viewport = document.getElementById('canvas_viewport');
|
||
|
var canvas_viewport_rect = canvas_viewport.getBoundingClientRect();
|
||
|
var viewport_height = px_string_to_int(canvas_viewport.style.height);
|
||
|
var viewport_height_no_scroll = viewport_height - k_scroll_width;
|
||
|
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
var canvas_rendering_res_x = subresource_extent['x'];
|
||
|
var canvas_rendering_res_y = subresource_extent['y'];
|
||
|
|
||
|
if (fix_texel_x === undefined || fix_texel_y === undefined)
|
||
|
{
|
||
|
fix_texel_x = canvas_rendering_res_x / 2;
|
||
|
fix_texel_y = canvas_rendering_res_y / 2;
|
||
|
}
|
||
|
|
||
|
// Compute the pixel coordinate of the fixed texel.
|
||
|
var fix_texel_pixel_pos_x;
|
||
|
var fix_texel_pixel_pos_y;
|
||
|
{
|
||
|
var rect = this.canvas.getBoundingClientRect();
|
||
|
|
||
|
fix_texel_pixel_pos_x = rect.left + rect.width * fix_texel_x / canvas_rendering_res_x - canvas_viewport_rect.left;
|
||
|
fix_texel_pixel_pos_y = rect.top + rect.height * fix_texel_y / canvas_rendering_res_y - canvas_viewport_rect.top;
|
||
|
}
|
||
|
|
||
|
// Compute new canvas dimensions
|
||
|
var canvas_display_w = new_pixel_scaling * canvas_rendering_res_x / dpi;
|
||
|
var canvas_display_h = new_pixel_scaling * canvas_rendering_res_y / dpi;
|
||
|
if (new_pixel_scaling == 0)
|
||
|
{
|
||
|
canvas_display_w = viewport_width_no_scroll;
|
||
|
canvas_display_h = viewport_width_no_scroll * canvas_rendering_res_y / canvas_rendering_res_x;
|
||
|
}
|
||
|
|
||
|
// Compute new canvas scroll such that the fixed texel ends up at the same pixel coordinate.
|
||
|
var canvas_scroll_x;
|
||
|
var canvas_scroll_y;
|
||
|
{
|
||
|
canvas_scroll_x = fix_texel_x * canvas_display_w / canvas_rendering_res_x - fix_texel_pixel_pos_x;
|
||
|
canvas_scroll_y = fix_texel_y * canvas_display_h / canvas_rendering_res_y - fix_texel_pixel_pos_y;
|
||
|
}
|
||
|
|
||
|
this.canvas.style.width = `${canvas_display_w}px`;
|
||
|
this.canvas.style.height = `${canvas_display_h}px`;
|
||
|
canvas_viewport.scrollLeft = canvas_scroll_x;
|
||
|
canvas_viewport.scrollTop = canvas_scroll_y;
|
||
|
this.pixel_scaling = new_pixel_scaling;
|
||
|
}
|
||
|
|
||
|
get_zoom_settings()
|
||
|
{
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
|
||
|
var canvas_viewport = document.getElementById('canvas_viewport');
|
||
|
|
||
|
var zoom_settings = {
|
||
|
'subresource_extent_x': subresource_extent['x'],
|
||
|
'subresource_extent_y': subresource_extent['y'],
|
||
|
'pixel_scaling': this.pixel_scaling,
|
||
|
'canvas_width': this.canvas.style.width,
|
||
|
'canvas_height': this.canvas.style.height,
|
||
|
'canvas_viewport_scoll_left': canvas_viewport.scrollLeft,
|
||
|
'canvas_viewport_scoll_top': canvas_viewport.scrollTop,
|
||
|
};
|
||
|
return zoom_settings;
|
||
|
}
|
||
|
|
||
|
is_zoom_settings_compatible(zoom_settings)
|
||
|
{
|
||
|
var subresource_extent = this.get_subresource_extent();
|
||
|
|
||
|
return (zoom_settings['subresource_extent_x'] == subresource_extent['x'] && zoom_settings['subresource_extent_y'] == subresource_extent['y']);
|
||
|
}
|
||
|
|
||
|
apply_zoom_settings(zoom_settings)
|
||
|
{
|
||
|
var canvas_viewport = document.getElementById('canvas_viewport');
|
||
|
|
||
|
this.pixel_scaling = zoom_settings['pixel_scaling'];
|
||
|
this.canvas.style.width = zoom_settings['canvas_width'];
|
||
|
this.canvas.style.height = zoom_settings['canvas_height'];
|
||
|
canvas_viewport.scrollLeft = zoom_settings['canvas_viewport_scoll_left'];
|
||
|
canvas_viewport.scrollTop = zoom_settings['canvas_viewport_scoll_top'];
|
||
|
|
||
|
this.update_change_pixel_scaling_button();
|
||
|
}
|
||
|
|
||
|
init_gl()
|
||
|
{
|
||
|
var gl_ctx_attributes = {
|
||
|
antialias: false,
|
||
|
depth: false
|
||
|
};
|
||
|
|
||
|
// Init WebGL 2.0
|
||
|
this.canvas = document.getElementById('texture_visualization_canvas');
|
||
|
|
||
|
var gl_ctx_attributes = {
|
||
|
alpha: false,
|
||
|
// premultipliedAlpha: true, // TODO
|
||
|
antialias: false,
|
||
|
depth: false,
|
||
|
stencil: false,
|
||
|
powerPreference: "low-power",
|
||
|
preserveDrawingBuffer: true,
|
||
|
xrCompatible: false,
|
||
|
};
|
||
|
|
||
|
var gl = this.canvas.getContext('webgl2', gl_ctx_attributes);
|
||
|
this.gl = gl;
|
||
|
|
||
|
// Init WebGL extensions
|
||
|
{
|
||
|
var available_extensions = this.gl.getSupportedExtensions();
|
||
|
var required_extensions = ['EXT_texture_norm16'];
|
||
|
|
||
|
this.gl_ext = {};
|
||
|
|
||
|
var gl = this.gl;
|
||
|
var gl_ext = this.gl_ext;
|
||
|
required_extensions.forEach(function(ext_name)
|
||
|
{
|
||
|
gl_ext[ext_name] = gl.getExtension(ext_name);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Set unpacking alignement to 1 to allow uploading texture size not multple of 4
|
||
|
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||
|
|
||
|
// Create vertex buffer
|
||
|
{
|
||
|
var vertices = [
|
||
|
-1.0,+1.0,0.0,
|
||
|
-1.0,-1.0,0.0,
|
||
|
+1.0,-1.0,0.0,
|
||
|
+1.0,+1.0,0.0,
|
||
|
];
|
||
|
|
||
|
this.gl_vertex_buffer = gl.createBuffer();
|
||
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.gl_vertex_buffer);
|
||
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
||
|
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||
|
}
|
||
|
|
||
|
// Create vertex buffer
|
||
|
{
|
||
|
var indices = [0, 1, 2, 2, 0, 3];
|
||
|
|
||
|
this.gl_index_buffer = gl.createBuffer();
|
||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gl_index_buffer);
|
||
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
|
||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
||
|
}
|
||
|
|
||
|
// Create simple shader program for NaN display mode
|
||
|
{
|
||
|
var frag_code = `
|
||
|
uniform sampler2D texture0;
|
||
|
|
||
|
in vec2 uv;
|
||
|
out vec4 display;
|
||
|
|
||
|
void main(void) {
|
||
|
ivec2 texel_coord = ivec2(uv * vec2(textureSize(texture0, 0)));
|
||
|
display = texelFetch(texture0, texel_coord, 0);
|
||
|
}`;
|
||
|
this.gl_simple_program = this.compile_screen_shader_program(frag_code);
|
||
|
}
|
||
|
|
||
|
// Create simple shader program for NaN display mode
|
||
|
{
|
||
|
var frag_code = `
|
||
|
uniform sampler2D texture0;
|
||
|
|
||
|
in vec2 uv;
|
||
|
out vec4 display;
|
||
|
|
||
|
void main(void)
|
||
|
{
|
||
|
ivec2 texel_coord = ivec2(uv * vec2(textureSize(texture0, 0)));
|
||
|
vec4 raw = texelFetch(texture0, texel_coord, 0);
|
||
|
|
||
|
display.r = (isnan(raw.r) || isnan(raw.g) || isnan(raw.b) || isnan(raw.a)) ? 1.0 : 0.0;
|
||
|
display.g = 0.0;
|
||
|
display.b = 0.0;
|
||
|
display.a = 1.0;
|
||
|
}`;
|
||
|
this.gl_nan_program = this.compile_screen_shader_program(frag_code);
|
||
|
}
|
||
|
|
||
|
// Create simple shader program for Inf display mode
|
||
|
{
|
||
|
var frag_code = `
|
||
|
uniform sampler2D texture0;
|
||
|
|
||
|
in vec2 uv;
|
||
|
out vec4 display;
|
||
|
|
||
|
void main(void)
|
||
|
{
|
||
|
ivec2 texel_coord = ivec2(uv * vec2(textureSize(texture0, 0)));
|
||
|
vec4 raw = texelFetch(texture0, texel_coord, 0);
|
||
|
|
||
|
display.r = (isinf(raw.r) || isinf(raw.g) || isinf(raw.b) || isinf(raw.a)) ? 1.0 : 0.0;
|
||
|
display.g = 0.0;
|
||
|
display.b = 0.0;
|
||
|
display.a = 1.0;
|
||
|
}`;
|
||
|
this.gl_inf_program = this.compile_screen_shader_program(frag_code);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
release_gl()
|
||
|
{
|
||
|
this.canvas = null;
|
||
|
this.gl = null;
|
||
|
this.gl_ext = null;
|
||
|
this.gl_vertex_buffer = null;
|
||
|
this.gl_index_buffer = null;
|
||
|
this.gl_simple_program = null;
|
||
|
this.gl_nan_program = null;
|
||
|
this.gl_textures = null;
|
||
|
}
|
||
|
|
||
|
release()
|
||
|
{
|
||
|
this.is_ready = false;
|
||
|
this.raw_texture_data = null;
|
||
|
this.release_gl();
|
||
|
this.set_select_viewport(false);
|
||
|
}
|
||
|
|
||
|
shader_user_code_change()
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.refresh_texture_visualization();
|
||
|
}
|
||
|
|
||
|
copy_canvas_to_clipboard()
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var texture_view = this;
|
||
|
this.canvas.toBlob(function(image_blob) {
|
||
|
var pass_data = g_view.pass_data;
|
||
|
|
||
|
var is_output_resource = pass_data['OutputResources'].includes(get_subresource_unique_name(texture_view.subresource_version_info));
|
||
|
|
||
|
var text = '';
|
||
|
text += `Dump:\n\t${g_infos['Project']} - ${g_infos['Platform']} - ${g_infos['RHI']} - ${g_infos['BuildVersion']} - ${g_infos['DumpTime']}\n\t${get_current_navigation()}\n`
|
||
|
text += `Pass:\n\t${pass_data['EventName']}\n`;
|
||
|
|
||
|
{
|
||
|
var name = prettify_subresource_unique_name(texture_view.subresource_version_info, texture_view.resource_desc);
|
||
|
text += `${is_output_resource ? 'Output' : 'Input'} resource:\n\t${name}\n`;
|
||
|
}
|
||
|
|
||
|
if (texture_view.subresource_version_info['draw'] >= 0)
|
||
|
{
|
||
|
text += `Draw ${texture_view.subresource_version_info['draw']}:\n\t${g_view.pass_draws_data[texture_view.subresource_version_info['draw']]['DrawName']}\n`;
|
||
|
}
|
||
|
|
||
|
var text_blob = new Blob([text], { type: 'text/plain' });
|
||
|
var clipboard_data = {
|
||
|
[text_blob.type]: text_blob,
|
||
|
[image_blob.type]: image_blob,
|
||
|
};
|
||
|
|
||
|
navigator.clipboard.write([new ClipboardItem(clipboard_data)]);
|
||
|
}, 'image/png');
|
||
|
}
|
||
|
|
||
|
get shader_code_saving_key()
|
||
|
{
|
||
|
var name = '';
|
||
|
if (this.resource_desc['Name'])
|
||
|
{
|
||
|
name = this.resource_desc['Name'];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
name = this.subresource_version_info['resource'];
|
||
|
}
|
||
|
|
||
|
if (this.subresource_version_info['subresource'] && !this.subresource_version_info['subresource'].startsWith('mip'))
|
||
|
{
|
||
|
name = `${name}.${this.subresource_version_info['subresource']}`;
|
||
|
}
|
||
|
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
get_default_texture_shader_code()
|
||
|
{
|
||
|
if (this.shader_code_saving_key in g_shader_code_dict)
|
||
|
{
|
||
|
return g_shader_code_dict[this.shader_code_saving_key];
|
||
|
}
|
||
|
|
||
|
if (!this.subresource_desc)
|
||
|
{
|
||
|
return `// Sorry but ${this.resource_desc['Format']} is not supported for display :(`;
|
||
|
}
|
||
|
|
||
|
return this.subresource_desc['webgl_pixel_shader'];
|
||
|
}
|
||
|
|
||
|
translate_subresource_desc()
|
||
|
{
|
||
|
var gl = this.gl;
|
||
|
var ue_pixel_format = this.resource_desc['Format'];
|
||
|
|
||
|
// Missing formats:
|
||
|
//PF_R8G8B8A8_SNORM
|
||
|
//PF_R16G16B16A16_UNORM
|
||
|
//PF_R16G16B16A16_SNORN
|
||
|
|
||
|
if (this.resource_desc['NumSamples'] > 1)
|
||
|
{
|
||
|
this.error(`MSAA is not supported for visualization.`);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var webgl_texture_internal_format = null;
|
||
|
var webgl_texture_src_format = null;
|
||
|
var webgl_texture_src_type = null;
|
||
|
var webgl_texture_count = 1;
|
||
|
var raw_channel_swizzling = [0, 1, 2, 3];
|
||
|
var raw_channel_bit_depth = 0;
|
||
|
var raw_channel_count = 0;
|
||
|
var raw_channel_format = 'exotic';
|
||
|
|
||
|
// 32bit floats
|
||
|
if (ue_pixel_format === 'PF_A32B32G32R32F')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA32F;
|
||
|
webgl_texture_src_format = gl.RGBA;
|
||
|
webgl_texture_src_type = gl.FLOAT;
|
||
|
raw_channel_bit_depth = 32;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_G32R32F')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RG32F;
|
||
|
webgl_texture_src_format = gl.RG;
|
||
|
webgl_texture_src_type = gl.FLOAT;
|
||
|
raw_channel_bit_depth = 32;
|
||
|
raw_channel_count = 2;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R32_FLOAT' || ue_pixel_format === 'PF_BC4')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R32F;
|
||
|
webgl_texture_src_format = gl.RED;
|
||
|
webgl_texture_src_type = gl.FLOAT;
|
||
|
raw_channel_bit_depth = 32;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
// 16bit floats
|
||
|
else if (ue_pixel_format === 'PF_FloatRGBA' || ue_pixel_format === 'PF_BC6H' || ue_pixel_format === 'PF_BC7')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA16F;
|
||
|
webgl_texture_src_format = gl.RGBA;
|
||
|
webgl_texture_src_type = gl.HALF_FLOAT;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_G16R16F' || ue_pixel_format === 'PF_G16R16F_FILTER' || ue_pixel_format === 'PF_BC5')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RG16F;
|
||
|
webgl_texture_src_format = gl.RG;
|
||
|
webgl_texture_src_type = gl.HALF_FLOAT;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 2;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R16F' || ue_pixel_format === 'PF_R16F_FILTER')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R16F;
|
||
|
webgl_texture_src_format = gl.RED;
|
||
|
webgl_texture_src_type = gl.HALF_FLOAT;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
// 16bit unorms
|
||
|
else if (ue_pixel_format === 'PF_A16B16G16R16')
|
||
|
{
|
||
|
webgl_texture_internal_format = this.gl_ext['EXT_texture_norm16'].RGBA16_EXT;
|
||
|
webgl_texture_src_format = gl.RGBA;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_SHORT;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'unorm';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_G16R16')
|
||
|
{
|
||
|
webgl_texture_internal_format = this.gl_ext['EXT_texture_norm16'].RG16_EXT;
|
||
|
webgl_texture_src_format = gl.RG;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_SHORT;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 2;
|
||
|
raw_channel_format = 'unorm';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_G16')
|
||
|
{
|
||
|
webgl_texture_internal_format = this.gl_ext['EXT_texture_norm16'].R16_EXT;
|
||
|
webgl_texture_src_format = gl.RED;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_SHORT;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'unorm';
|
||
|
}
|
||
|
// 8bit unorms
|
||
|
else if (ue_pixel_format === 'PF_B8G8R8A8' || ue_pixel_format === 'PF_R8G8B8A8')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA;
|
||
|
webgl_texture_src_format = gl.RGBA;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
raw_channel_bit_depth = 8;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'unorm';
|
||
|
|
||
|
if (ue_pixel_format === 'PF_B8G8R8A8' && g_infos['RHI'] !== 'OpenGL')
|
||
|
{
|
||
|
raw_channel_swizzling = [2, 1, 0, 3];
|
||
|
}
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R8G8')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RG8;
|
||
|
webgl_texture_src_format = gl.RG;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
raw_channel_bit_depth = 8;
|
||
|
raw_channel_count = 2;
|
||
|
raw_channel_format = 'unorm';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R8' || ue_pixel_format === 'PF_G8' || ue_pixel_format === 'PF_A8' || ue_pixel_format === 'PF_L8')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R8;
|
||
|
webgl_texture_src_format = gl.RED;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
raw_channel_bit_depth = 8;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'unorm';
|
||
|
}
|
||
|
// 32bit uint
|
||
|
else if (ue_pixel_format === 'PF_R32_UINT' || ue_pixel_format === 'PF_R32_SINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R8UI;
|
||
|
webgl_texture_src_format = gl.RED_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
webgl_texture_count = 4;
|
||
|
raw_channel_bit_depth = 32;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R32G32B32A32_UINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA8UI;
|
||
|
webgl_texture_src_format = gl.RGBA_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
webgl_texture_count = 4;
|
||
|
raw_channel_bit_depth = 32;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R32G32_UINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA8UI;
|
||
|
webgl_texture_src_format = gl.RGBA_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
webgl_texture_count = 4;
|
||
|
raw_channel_bit_depth = 32;
|
||
|
raw_channel_count = 2;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
// 16bit uint
|
||
|
else if (ue_pixel_format === 'PF_R16_UINT' || ue_pixel_format === 'PF_R16_SINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R8UI;
|
||
|
webgl_texture_src_format = gl.RED_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
webgl_texture_count = 2;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R16G16B16A16_UINT' || ue_pixel_format === 'PF_R16G16B16A16_SINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA8UI;
|
||
|
webgl_texture_src_format = gl.RGBA_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
webgl_texture_count = 2;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R16G16_UINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA8UI;
|
||
|
webgl_texture_src_format = gl.RGBA_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
webgl_texture_count = 2;
|
||
|
raw_channel_bit_depth = 16;
|
||
|
raw_channel_count = 2;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
// 8bit uint
|
||
|
else if (ue_pixel_format === 'PF_R8G8B8A8_UINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGBA8UI;
|
||
|
webgl_texture_src_format = gl.RGBA_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
raw_channel_bit_depth = 8;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R8_UINT')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R8UI;
|
||
|
webgl_texture_src_format = gl.RED_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
raw_channel_bit_depth = 8;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
// exotic bit depth
|
||
|
else if (ue_pixel_format === 'PF_FloatRGB' || ue_pixel_format === 'PF_FloatR11G11B10')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R11F_G11F_B10F;
|
||
|
webgl_texture_src_format = gl.RGB;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_INT_10F_11F_11F_REV;
|
||
|
raw_channel_bit_depth = k_exotic_raw_channel_bit_depth;
|
||
|
raw_channel_count = 3;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_A2B10G10R10')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGB10_A2;
|
||
|
webgl_texture_src_format = gl.RGBA;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_INT_2_10_10_10_REV;
|
||
|
raw_channel_bit_depth = k_exotic_raw_channel_bit_depth;
|
||
|
raw_channel_count = 4;
|
||
|
raw_channel_format = 'unorm';
|
||
|
}
|
||
|
else if (ue_pixel_format === 'PF_R5G6B5_UNORM')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.RGB565;
|
||
|
webgl_texture_src_format = gl.RGB;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_SHORT_5_6_5;
|
||
|
raw_channel_bit_depth = k_exotic_raw_channel_bit_depth;
|
||
|
raw_channel_count = 3;
|
||
|
raw_channel_format = 'unorm';
|
||
|
}
|
||
|
// depth stencil
|
||
|
else if (ue_pixel_format === 'PF_DepthStencil' || ue_pixel_format === 'PF_ShadowDepth' || ue_pixel_format === 'PF_D24')
|
||
|
{
|
||
|
if (this.subresource_version_info['subresource'] == 'stencil')
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R8UI;
|
||
|
webgl_texture_src_format = gl.RED_INTEGER;
|
||
|
webgl_texture_src_type = gl.UNSIGNED_BYTE;
|
||
|
raw_channel_bit_depth = 8;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'uint';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
webgl_texture_internal_format = gl.R32F;
|
||
|
webgl_texture_src_format = gl.RED;
|
||
|
webgl_texture_src_type = gl.FLOAT;
|
||
|
raw_channel_bit_depth = 32;
|
||
|
raw_channel_count = 1;
|
||
|
raw_channel_format = 'float';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.error(`Pixel format ${ue_pixel_format} is not supported for visualization.`);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var shader_float_vector = [null, 'float', 'vec2', 'vec3', 'vec4'];
|
||
|
|
||
|
var shader_sampler_type = 'sampler2D';
|
||
|
var shader_sampler_return_type = shader_float_vector;
|
||
|
var shader_display_operation = 'texel';
|
||
|
if (webgl_texture_src_format === gl.RED_INTEGER || webgl_texture_src_format === gl.RG_INTEGER || webgl_texture_src_format === gl.RGBA_INTEGER)
|
||
|
{
|
||
|
var divide = (BigInt(1) << BigInt(raw_channel_bit_depth)) - 1n;
|
||
|
shader_sampler_type = 'usampler2D';
|
||
|
shader_sampler_return_type = [null, 'uint', 'uvec2', 'uvec3', 'uvec4'];
|
||
|
shader_display_operation = `${shader_float_vector[raw_channel_count]}(${shader_display_operation}) / ${divide}.0`;
|
||
|
}
|
||
|
|
||
|
var webgl_channel_count = 0;
|
||
|
if (webgl_texture_src_format === gl.RED || webgl_texture_src_format === gl.RED_INTEGER)
|
||
|
{
|
||
|
webgl_channel_count = 1;
|
||
|
}
|
||
|
else if (webgl_texture_src_format === gl.RG)
|
||
|
{
|
||
|
webgl_channel_count = 2;
|
||
|
}
|
||
|
else if (webgl_texture_src_format === gl.RGB)
|
||
|
{
|
||
|
webgl_channel_count = 3;
|
||
|
}
|
||
|
else if (webgl_texture_src_format === gl.RGBA || webgl_texture_src_format === gl.RGBA_INTEGER)
|
||
|
{
|
||
|
webgl_channel_count = 4;
|
||
|
}
|
||
|
|
||
|
var webgl_pixel_shader_private = ``;
|
||
|
|
||
|
var shader_fetch_operation = '(\n';
|
||
|
for (var i = 0; i < webgl_texture_count; i++)
|
||
|
{
|
||
|
webgl_pixel_shader_private += `uniform ${shader_sampler_type} texture${i};\n`;
|
||
|
|
||
|
shader_fetch_operation += `\t\t(texelFetch(texture${i}, texel_coord, 0) << ${8 * i})`;
|
||
|
if (i + 1 == webgl_texture_count)
|
||
|
{
|
||
|
shader_fetch_operation += `)`;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
shader_fetch_operation += ` |\n`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (webgl_texture_count == 1)
|
||
|
{
|
||
|
shader_fetch_operation = `texelFetch(texture0, texel_coord , 0)`;
|
||
|
}
|
||
|
|
||
|
const k_get_n_channels = new Array(
|
||
|
'',
|
||
|
'.r',
|
||
|
'.rg',
|
||
|
'.rgb',
|
||
|
'.rgba'
|
||
|
);
|
||
|
|
||
|
var shader_display_assignment = `display${k_get_n_channels[raw_channel_count]}`;
|
||
|
|
||
|
webgl_pixel_shader_private += `
|
||
|
${shader_sampler_return_type[raw_channel_count]} fetchTexel(vec2 uv)
|
||
|
{
|
||
|
ivec2 texel_coord = ivec2(uv * vec2(textureSize(texture0, 0)));
|
||
|
${shader_sampler_return_type[4]} raw = ${shader_fetch_operation};
|
||
|
return raw${k_get_n_channels[raw_channel_count]};
|
||
|
}
|
||
|
|
||
|
in vec2 uv;
|
||
|
out vec4 display;
|
||
|
`;
|
||
|
|
||
|
var webgl_pixel_shader_public = `// WebGL 2.0 visualization shader for a ${ue_pixel_format} ${this.subresource_version_info['subresource']}
|
||
|
|
||
|
${shader_sampler_return_type[raw_channel_count]} texel = fetchTexel(uv);
|
||
|
${shader_display_assignment} = ${shader_display_operation};`;
|
||
|
|
||
|
var gl_format = {};
|
||
|
gl_format['webgl_internal_format'] = webgl_texture_internal_format;
|
||
|
gl_format['webgl_src_format'] = webgl_texture_src_format;
|
||
|
gl_format['webgl_src_type'] = webgl_texture_src_type;
|
||
|
gl_format['webgl_texture_count'] = webgl_texture_count;
|
||
|
gl_format['webgl_pixel_shader_public'] = webgl_pixel_shader_public;
|
||
|
gl_format['webgl_pixel_shader_private'] = webgl_pixel_shader_private;
|
||
|
gl_format['webgl_channel_count'] = webgl_channel_count;
|
||
|
gl_format['raw_channel_swizzling'] = raw_channel_swizzling;
|
||
|
gl_format['raw_channel_bit_depth'] = raw_channel_bit_depth;
|
||
|
gl_format['raw_channel_count'] = raw_channel_count;
|
||
|
gl_format['raw_channel_format'] = raw_channel_format;
|
||
|
gl_format['ue_pixel_format'] = ue_pixel_format;
|
||
|
|
||
|
return gl_format;
|
||
|
}
|
||
|
|
||
|
change_pixel_scaling(pixel_scaling_button)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const pixel_scalings = {
|
||
|
'Fit': 0,
|
||
|
'1:1': 1,
|
||
|
'2:1': 2,
|
||
|
'4:1': 4,
|
||
|
'8:1': 8,
|
||
|
'16:1': 16,
|
||
|
};
|
||
|
this.resize_canvas(pixel_scalings[pixel_scaling_button.value]);
|
||
|
update_value_selection(document.getElementById('texture_visualization_change_pixel_scaling'), pixel_scaling_button.value);
|
||
|
}
|
||
|
|
||
|
change_display_mode(new_mode)
|
||
|
{
|
||
|
if (!this.is_ready)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (new_mode.value == this.display_mode)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.display_mode = new_mode.value;
|
||
|
|
||
|
update_value_selection(document.getElementById('texture_visualization_change_display_modes'), this.display_mode);
|
||
|
|
||
|
if (this.display_mode == 'Visualization')
|
||
|
{
|
||
|
document.getElementById('texture_visualization_code_input').readOnly = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.getElementById('texture_visualization_code_input').readOnly = true;
|
||
|
}
|
||
|
|
||
|
this.refresh_texture_visualization();
|
||
|
}
|
||
|
|
||
|
get_subresource_extent()
|
||
|
{
|
||
|
var extent = {};
|
||
|
extent['x'] = this.resource_desc['ExtentX'];
|
||
|
extent['y'] = this.resource_desc['ExtentY'];
|
||
|
|
||
|
if (this.subresource_version_info['subresource'].startsWith('mip'))
|
||
|
{
|
||
|
var mip_id = Number(this.subresource_version_info['subresource'].substring(3));
|
||
|
|
||
|
for (var i = 0; i < mip_id; i++)
|
||
|
{
|
||
|
extent['x'] = Math.floor(extent['x'] / 2);
|
||
|
extent['y'] = Math.floor(extent['y'] / 2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return extent;
|
||
|
}
|
||
|
|
||
|
create_gl_texture(webgl_internal_format, webgl_src_format, webgl_src_type, extent, converted_data)
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
var gl = this.gl;
|
||
|
|
||
|
var texture = gl.createTexture();
|
||
|
gl.activeTexture(gl.TEXTURE0);
|
||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||
|
gl.texImage2D(
|
||
|
gl.TEXTURE_2D,
|
||
|
/* level = */ 0,
|
||
|
webgl_internal_format,
|
||
|
extent['x'],
|
||
|
extent['y'],
|
||
|
/* border = */ 0,
|
||
|
webgl_src_format,
|
||
|
webgl_src_type,
|
||
|
converted_data);
|
||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
||
|
|
||
|
return texture;
|
||
|
}
|
||
|
|
||
|
process_texture_data_for_visualization()
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
var gl = this.gl;
|
||
|
|
||
|
if (this.raw_texture_data === null)
|
||
|
{
|
||
|
console.error(`failed to load ${get_subresource_unique_version_name(this.subresource_version_info)}`);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var extent = this.get_subresource_extent();
|
||
|
var webgl_array_size = this.subresource_desc['webgl_channel_count'] * extent['x'] * extent['y'];
|
||
|
|
||
|
// Reset the gl textures
|
||
|
this.gl_textures = [];
|
||
|
|
||
|
// Exotic pixel format
|
||
|
if (this.subresource_desc['raw_channel_bit_depth'] == k_exotic_raw_channel_bit_depth)
|
||
|
{
|
||
|
var gl = this.gl;
|
||
|
var original_converted_data = null;
|
||
|
if (this.subresource_desc['webgl_src_type'] === gl.UNSIGNED_SHORT_5_6_5)
|
||
|
{
|
||
|
original_converted_data = new Uint16Array(this.raw_texture_data);
|
||
|
}
|
||
|
else if (this.subresource_desc['webgl_src_type'] === gl.UNSIGNED_INT_10F_11F_11F_REV || this.subresource_desc['webgl_src_type'] === gl.UNSIGNED_INT_2_10_10_10_REV)
|
||
|
{
|
||
|
original_converted_data = new Uint32Array(this.raw_texture_data);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var texture = this.create_gl_texture(this.subresource_desc['webgl_internal_format'], this.subresource_desc['webgl_src_format'], this.subresource_desc['webgl_src_type'], extent, original_converted_data);
|
||
|
this.gl_textures.push(texture);
|
||
|
}
|
||
|
else if (this.subresource_desc['webgl_texture_count'] > 1)
|
||
|
{
|
||
|
var original_converted_data = null;
|
||
|
if (this.subresource_desc['raw_channel_bit_depth'] === 8)
|
||
|
{
|
||
|
original_converted_data = new Uint8Array(this.raw_texture_data);
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_bit_depth'] === 16)
|
||
|
{
|
||
|
original_converted_data = new Uint16Array(this.raw_texture_data);
|
||
|
}
|
||
|
else if (this.subresource_desc['raw_channel_bit_depth'] === 32)
|
||
|
{
|
||
|
original_converted_data = new Uint32Array(this.raw_texture_data);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
for (var tex_id = 0; tex_id < this.subresource_desc['webgl_texture_count']; tex_id++)
|
||
|
{
|
||
|
var converted_data = new Uint8Array(webgl_array_size);
|
||
|
|
||
|
for (var y = 0; y < extent['y']; y++)
|
||
|
{
|
||
|
for (var x = 0; x < extent['x']; x++)
|
||
|
{
|
||
|
for (var c = 0; c < this.subresource_desc['webgl_channel_count']; c++)
|
||
|
{
|
||
|
var texel_offset = x + extent['x'] * y;
|
||
|
|
||
|
var value = 0;
|
||
|
if (c < this.subresource_desc['raw_channel_count'])
|
||
|
{
|
||
|
var src = original_converted_data[texel_offset * this.subresource_desc['raw_channel_count'] + this.subresource_desc['raw_channel_swizzling'][c]];
|
||
|
value = (src >> (8 * tex_id)) % 256;
|
||
|
}
|
||
|
converted_data[texel_offset * this.subresource_desc['webgl_channel_count'] + c] = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var texture = this.create_gl_texture(this.subresource_desc['webgl_internal_format'], this.subresource_desc['webgl_src_format'], this.subresource_desc['webgl_src_type'], extent, converted_data);
|
||
|
this.gl_textures.push(texture);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var gl = this.gl;
|
||
|
var converted_data = null;
|
||
|
var original_converted_data = null;
|
||
|
if (this.subresource_desc['webgl_src_type'] === gl.UNSIGNED_BYTE)
|
||
|
{
|
||
|
original_converted_data = new Uint8Array(this.raw_texture_data);
|
||
|
converted_data = new Uint8Array(webgl_array_size);
|
||
|
}
|
||
|
else if (this.subresource_desc['webgl_src_type'] === gl.UNSIGNED_SHORT || this.subresource_desc['webgl_src_type'] === gl.HALF_FLOAT)
|
||
|
{
|
||
|
original_converted_data = new Uint16Array(this.raw_texture_data);
|
||
|
converted_data = new Uint16Array(webgl_array_size);
|
||
|
}
|
||
|
else if (this.subresource_desc['webgl_src_type'] === gl.FLOAT)
|
||
|
{
|
||
|
original_converted_data = new Float32Array(this.raw_texture_data);
|
||
|
converted_data = new Float32Array(webgl_array_size);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
for (var y = 0; y < extent['y']; y++)
|
||
|
{
|
||
|
for (var x = 0; x < extent['x']; x++)
|
||
|
{
|
||
|
for (var c = 0; c < this.subresource_desc['webgl_channel_count']; c++)
|
||
|
{
|
||
|
var i = this.subresource_desc['webgl_channel_count'] * (x + extent['x'] * y);
|
||
|
|
||
|
var value = 0;
|
||
|
if (c < this.subresource_desc['raw_channel_count'])
|
||
|
{
|
||
|
value = original_converted_data[i + this.subresource_desc['raw_channel_swizzling'][c]];
|
||
|
}
|
||
|
converted_data[i + c] = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var texture = this.create_gl_texture(this.subresource_desc['webgl_internal_format'], this.subresource_desc['webgl_src_format'], this.subresource_desc['webgl_src_type'], extent, converted_data);
|
||
|
this.gl_textures.push(texture);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
compile_screen_shader_program(frag_code)
|
||
|
{
|
||
|
var gl = this.gl;
|
||
|
|
||
|
//console.log(frag_code);
|
||
|
|
||
|
const shader_std = `#version 300 es
|
||
|
|
||
|
precision highp float;
|
||
|
precision highp int;
|
||
|
precision highp sampler2D;
|
||
|
precision highp usampler2D;
|
||
|
|
||
|
struct FloatInfos
|
||
|
{
|
||
|
bool is_denormal;
|
||
|
bool is_infinity;
|
||
|
bool is_nan;
|
||
|
};
|
||
|
|
||
|
FloatInfos decodeFloat(float x)
|
||
|
{
|
||
|
const uint mantissa_bit_count = 23u;
|
||
|
const uint exp_bit_count = 8u;
|
||
|
|
||
|
uint raw = floatBitsToUint(x);
|
||
|
|
||
|
uint sign_bit = (raw >> (mantissa_bit_count + exp_bit_count)) & 0x1u;
|
||
|
uint mantissa_bits = (raw >> 0u) & ((0x1u << mantissa_bit_count) - 1u);
|
||
|
uint exp_bits = (raw >> mantissa_bit_count) & ((0x1u << exp_bit_count) - 1u);
|
||
|
|
||
|
bool is_max_exp = exp_bits == ((0x1u << exp_bit_count) - 1u);
|
||
|
|
||
|
FloatInfos infos;
|
||
|
infos.is_denormal = exp_bits == 0u;
|
||
|
infos.is_infinity = is_max_exp && mantissa_bits == 0u;
|
||
|
infos.is_nan = is_max_exp && mantissa_bits != 0u;
|
||
|
|
||
|
return infos;
|
||
|
}
|
||
|
|
||
|
#define isnan(x) decodeFloat(x).is_nan
|
||
|
#define isinf(x) decodeFloat(x).is_infinity
|
||
|
#define isdenorm(x) decodeFloat(x).is_denormal
|
||
|
|
||
|
`;
|
||
|
|
||
|
var vert_code = `
|
||
|
in vec3 coordinates;
|
||
|
out highp vec2 uv;
|
||
|
void main(void) {
|
||
|
gl_Position = vec4(coordinates, 1.0);
|
||
|
uv = coordinates.xy * 0.5 + 0.5;
|
||
|
}`;
|
||
|
|
||
|
var vert_shader = gl.createShader(gl.VERTEX_SHADER);
|
||
|
{
|
||
|
gl.shaderSource(vert_shader, shader_std + vert_code);
|
||
|
gl.compileShader(vert_shader);
|
||
|
|
||
|
var compilation_status = gl.getShaderParameter(vert_shader, gl.COMPILE_STATUS);
|
||
|
if (!compilation_status)
|
||
|
{
|
||
|
var compilation_log = gl.getShaderInfoLog(vert_shader);
|
||
|
console.error('Fragment shader compiler log: ' + compilation_log);
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var frag_shader = gl.createShader(gl.FRAGMENT_SHADER);
|
||
|
{
|
||
|
gl.shaderSource(frag_shader, shader_std + frag_code);
|
||
|
gl.compileShader(frag_shader);
|
||
|
|
||
|
var compilation_status = gl.getShaderParameter(frag_shader, gl.COMPILE_STATUS);
|
||
|
if (!compilation_status)
|
||
|
{
|
||
|
var compilation_log = gl.getShaderInfoLog(frag_shader);
|
||
|
console.log(frag_code);
|
||
|
console.error('Fragment shader compiler log: ' + compilation_log);
|
||
|
document.getElementById('texture_visualization_code_log').value = 'Compilation failed:\n' + compilation_log;
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var shader_program = gl.createProgram();
|
||
|
{
|
||
|
gl.attachShader(shader_program, vert_shader);
|
||
|
gl.attachShader(shader_program, frag_shader);
|
||
|
gl.linkProgram(shader_program);
|
||
|
|
||
|
var link_status = gl.getProgramParameter(shader_program, gl.LINK_STATUS);
|
||
|
if (!link_status)
|
||
|
{
|
||
|
var link_log = gl.getProgramInfoLog(shader_program);
|
||
|
console.error('Program compiler log: ' + link_log);
|
||
|
document.getElementById('texture_visualization_code_log').value = 'Link failed:\n' + link_log;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.getElementById('texture_visualization_code_log').value = 'Compilation succeeded';
|
||
|
}
|
||
|
}
|
||
|
return shader_program;
|
||
|
}
|
||
|
|
||
|
refresh_texture_visualization()
|
||
|
{
|
||
|
console.assert(this.is_ready);
|
||
|
console.assert(this.gl_textures);
|
||
|
|
||
|
var shader_program = null;
|
||
|
if (this.display_mode == 'NaN')
|
||
|
{
|
||
|
shader_program = this.gl_nan_program;
|
||
|
}
|
||
|
else if (this.display_mode == 'Inf')
|
||
|
{
|
||
|
shader_program = this.gl_inf_program;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var user_frag_code = document.getElementById('texture_visualization_code_input').value;
|
||
|
shader_program = this.compile_screen_shader_program(this.subresource_desc['webgl_pixel_shader_private'] + 'void main(void) { display = vec4(0.0, 0.0, 0.0, 0.0);\n' + user_frag_code + `\n}`);
|
||
|
|
||
|
if (!shader_program)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Save the user code if compilation have succeeded.
|
||
|
g_shader_code_dict[this.shader_code_saving_key] = user_frag_code;
|
||
|
}
|
||
|
|
||
|
var gl = this.gl;
|
||
|
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||
|
|
||
|
{
|
||
|
gl.useProgram(shader_program);
|
||
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.gl_vertex_buffer);
|
||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gl_index_buffer);
|
||
|
|
||
|
var coord = gl.getAttribLocation(shader_program, "coordinates");
|
||
|
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
|
||
|
gl.enableVertexAttribArray(coord);
|
||
|
|
||
|
for (var tex_id = 0; tex_id < this.gl_textures.length; tex_id++)
|
||
|
{
|
||
|
gl.activeTexture(gl.TEXTURE0 + tex_id);
|
||
|
gl.bindTexture(gl.TEXTURE_2D, this.gl_textures[tex_id]);
|
||
|
|
||
|
var texture_sampler = gl.getUniformLocation(shader_program, 'texture' + tex_id);
|
||
|
gl.uniform1i(texture_sampler, tex_id);
|
||
|
}
|
||
|
|
||
|
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
||
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
|
||
|
}
|
||
|
}
|
||
|
} // class TextureView
|
||
|
|
||
|
function display_texture(subresource_version_info)
|
||
|
{
|
||
|
if (g_view.resource_view !== null && get_subresource_unique_version_name(g_view.resource_view.subresource_version_info) == get_subresource_unique_version_name(subresource_version_info))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var resource_desc = get_resource_desc(subresource_version_info['resource']);
|
||
|
g_view.set_resource_view(new TextureView(subresource_version_info, resource_desc));
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- DISPLAY BUFFER
|
||
|
|
||
|
const k_buffer_pixel_per_row = 20;
|
||
|
const k_buffer_max_display_row_count = 50;
|
||
|
const k_buffer_display_row_buffer = 50;
|
||
|
|
||
|
|
||
|
class GenericBufferView extends ResourceView
|
||
|
{
|
||
|
constructor(subresource_version_info, resource_desc)
|
||
|
{
|
||
|
super(subresource_version_info, resource_desc);
|
||
|
this.raw_buffer_data = null;
|
||
|
this.raw_buffer_data_path = null;
|
||
|
this.viewport_selected = false;
|
||
|
this.display_row_start = 0;
|
||
|
this.selected_address = -1;
|
||
|
this.structure_metadata = null;
|
||
|
this.display_elem_using_rows = resource_desc['NumElements'] == 1;
|
||
|
}
|
||
|
|
||
|
setup_html(parent_dom)
|
||
|
{
|
||
|
var html = '';
|
||
|
|
||
|
if (this.resource_desc['Desc'] != 'FRDGParameterStruct')
|
||
|
{
|
||
|
var resource_info_htmls = `
|
||
|
<table width="100%" class="resource_desc pretty_table">`;
|
||
|
|
||
|
for (var key in this.resource_desc)
|
||
|
{
|
||
|
if (this.resource_desc.hasOwnProperty(key))
|
||
|
{
|
||
|
var value = this.resource_desc[key];
|
||
|
|
||
|
if (key == 'Metadata')
|
||
|
{
|
||
|
if (this.resource_desc[key] == k_null_json_ptr)
|
||
|
{
|
||
|
value = 'nullptr';
|
||
|
}
|
||
|
else if (this.structure_metadata)
|
||
|
{
|
||
|
value = `${this.structure_metadata['StructTypeName']} (${get_filename(this.structure_metadata['FileName'])}:${this.structure_metadata['FileLine']})`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resource_info_htmls += `
|
||
|
<tr>
|
||
|
<td>${key}</td>
|
||
|
<td>${value}</td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resource_info_htmls += `
|
||
|
</table>`;
|
||
|
|
||
|
html += `
|
||
|
<div class="main_div">
|
||
|
<div class="selection_list_title">Buffer descriptor: ${this.resource_desc['Name']}</div>
|
||
|
<div>
|
||
|
${resource_info_htmls}
|
||
|
</div>
|
||
|
</div>`;
|
||
|
}
|
||
|
|
||
|
html += `
|
||
|
<div class="main_div">
|
||
|
<div class="selection_list_title" style="width: auto;">Buffer visualization: ${this.resource_desc['Name']}</div>
|
||
|
<div id="buffer_outter">
|
||
|
<div id="buffer_viewport_header">
|
||
|
<table width="100%" id="buffer_visualization_format_outter">
|
||
|
<tr>
|
||
|
<td style="padding: 4px 20px 0 20px; text-align: right;" width="40px">Format:</td>
|
||
|
<td>
|
||
|
<input type="text" id="buffer_visualization_format" onchange="g_view.resource_view.refresh_buffer_visualization(true);" style="width: 100%;" />
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
<div id="buffer_visualization_member_search_outter">
|
||
|
<input type="search" id="buffer_visualization_member_search" oninput="g_view.resource_view.refresh_members();" onchange="g_view.resource_view.refresh_members();" style="width: 100%;" placeholder="Search member..." />
|
||
|
</div>
|
||
|
</div>
|
||
|
<div id="buffer_viewport">
|
||
|
<div id="buffer_content_pannel"></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>`;
|
||
|
|
||
|
parent_dom.innerHTML = html;
|
||
|
|
||
|
if (this.structure_metadata)
|
||
|
{
|
||
|
// document.getElementById('buffer_visualization_format').value = `${this.structure_metadata['StructTypeName']} (${get_filename(this.structure_metadata['FileName'])}:${this.structure_metadata['FileLine']})`;
|
||
|
// document.getElementById('buffer_visualization_format').readOnly = true;
|
||
|
document.getElementById('buffer_visualization_format_outter').style.display = 'none';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var default_format = '';
|
||
|
{
|
||
|
var byte_per_element = this.resource_desc['BytesPerElement'];
|
||
|
var member_count = byte_per_element / 4;
|
||
|
|
||
|
var default_format = 'hex(uint)';
|
||
|
|
||
|
// DrawIndirect flags means this is certainly a 32bit uint.
|
||
|
if ('Usage' in this.resource_desc && this.resource_desc['Usage'].includes('DrawIndirect'))
|
||
|
{
|
||
|
default_format = 'uint';
|
||
|
}
|
||
|
|
||
|
var format_per_column = [];
|
||
|
for (var i = 0; i < member_count; i++)
|
||
|
{
|
||
|
format_per_column.push(default_format);
|
||
|
}
|
||
|
|
||
|
default_format = format_per_column.join(', ');
|
||
|
}
|
||
|
|
||
|
document.getElementById('buffer_visualization_format').value = default_format;
|
||
|
document.getElementById('buffer_visualization_member_search_outter').style.display = 'none';
|
||
|
}
|
||
|
|
||
|
// Setup mouse callbacks
|
||
|
{
|
||
|
var buffer_view = this;
|
||
|
|
||
|
document.getElementById('buffer_outter').onclick = function(event) { buffer_view.onclick_buffer_outter(event); };
|
||
|
document.getElementById('buffer_outter').onmouseleave = function(event) { buffer_view.set_select_viewport(false); };
|
||
|
document.getElementById('buffer_viewport').onclick = function(event) { buffer_view.onclick_buffer_viewport(event); };
|
||
|
document.getElementById('buffer_viewport').onscroll = function(event) { buffer_view.update_buffer_scroll(/* force_refresh = */ false); };
|
||
|
}
|
||
|
|
||
|
var buffer_view = this;
|
||
|
console.assert(this.raw_buffer_data_path);
|
||
|
load_resource_binary_file(this.raw_buffer_data_path, function(raw_buffer_data)
|
||
|
{
|
||
|
if (raw_buffer_data)
|
||
|
{
|
||
|
buffer_view.raw_buffer_data = raw_buffer_data;
|
||
|
buffer_view.refresh_buffer_visualization(/* refresh header = */ true);
|
||
|
buffer_view.onload();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.getElementById('buffer_content_pannel').innerHTML = `
|
||
|
Error: ${this.raw_buffer_data_path} couldn't be loaded.`;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
document.title = `${g_view.pass_data['EventName']} - ${this.resource_desc['Name']}`;
|
||
|
}
|
||
|
|
||
|
refresh_members()
|
||
|
{
|
||
|
this.refresh_buffer_visualization(/* refresh_header = */ !this.display_elem_using_rows);
|
||
|
}
|
||
|
|
||
|
onclick_buffer_outter(event)
|
||
|
{
|
||
|
this.set_select_viewport(true);
|
||
|
|
||
|
if (this.display_elem_using_rows)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var content_rect = document.getElementById('buffer_content_pannel').getBoundingClientRect();
|
||
|
var header_rect = document.getElementById('buffer_content_header').getBoundingClientRect();
|
||
|
|
||
|
var html_x = event.clientX - content_rect.left;
|
||
|
var html_y = event.clientY - content_rect.top - header_rect.height;
|
||
|
|
||
|
var new_selected_address = Math.floor(html_y / k_buffer_pixel_per_row);
|
||
|
|
||
|
if (new_selected_address != this.selected_address && new_selected_address < this.resource_desc['NumElements'])
|
||
|
{
|
||
|
this.selected_address = new_selected_address;
|
||
|
this.refresh_buffer_visualization();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
onclick_buffer_viewport(event)
|
||
|
{
|
||
|
this.set_select_viewport(true);
|
||
|
}
|
||
|
|
||
|
set_select_viewport(selected)
|
||
|
{
|
||
|
if (this.viewport_selected === selected)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.viewport_selected = selected;
|
||
|
if (this.viewport_selected)
|
||
|
{
|
||
|
var buffer_view = this;
|
||
|
document.getElementById('buffer_outter').classList.add('selected');
|
||
|
document.getElementById('buffer_viewport').style.overflow = 'scroll';
|
||
|
//document.getElementById('buffer_viewport').style.margin = '0px 0px 0px 0px';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.getElementById('buffer_outter').classList.remove('selected');
|
||
|
document.getElementById('buffer_viewport').style.overflow = 'hidden';
|
||
|
//document.getElementById('buffer_viewport').style.margin = `0px ${k_scroll_width}px ${k_scroll_width}px 0px`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
update_buffer_scroll(force_refresh)
|
||
|
{
|
||
|
var buffer_viewport = document.getElementById('buffer_viewport');
|
||
|
|
||
|
var new_display_row_start = Math.max(2 * Math.floor((buffer_viewport.scrollTop / k_buffer_pixel_per_row) * 0.5), 0);
|
||
|
|
||
|
if (new_display_row_start != this.display_row_start || force_refresh)
|
||
|
{
|
||
|
this.display_row_start = new_display_row_start;
|
||
|
this.refresh_buffer_visualization(/* refresh_header = */ false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
go_to_address()
|
||
|
{
|
||
|
var byte_per_element = this.resource_desc['BytesPerElement'];
|
||
|
|
||
|
var address = document.getElementById('buffer_address_input').value;
|
||
|
if (address == '')
|
||
|
{
|
||
|
if (this.selected_address != -1)
|
||
|
{
|
||
|
this.selected_address = -1;
|
||
|
this.update_buffer_scroll(/* force_refresh = */ true);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var element_id = Math.floor(Number(address) / byte_per_element);
|
||
|
if (element_id < this.resource_desc['NumElements'])
|
||
|
{
|
||
|
this.selected_address = element_id;
|
||
|
|
||
|
var buffer_viewport = document.getElementById('buffer_viewport');
|
||
|
buffer_viewport.scrollTop = (element_id - 5) * k_buffer_pixel_per_row;
|
||
|
|
||
|
this.update_buffer_scroll(/* force_refresh = */ true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
refresh_buffer_visualization(refresh_header)
|
||
|
{
|
||
|
var byte_per_element = this.resource_desc['BytesPerElement'];
|
||
|
var num_element = this.resource_desc['NumElements'];
|
||
|
|
||
|
var member_count = 0;
|
||
|
var member_names = [];
|
||
|
var member_byte_offset = [];
|
||
|
var member_format = [];
|
||
|
if (this.structure_metadata)
|
||
|
{
|
||
|
var member_search = document.getElementById('buffer_visualization_member_search').value;
|
||
|
|
||
|
iterate_structure_members(this.structure_metadata, function(it) {
|
||
|
var column_format = undefined;
|
||
|
if (it.base_type == 'UBMT_INT32')
|
||
|
{
|
||
|
column_format = 'int';
|
||
|
}
|
||
|
else if (it.base_type == 'UBMT_UINT32')
|
||
|
{
|
||
|
column_format = 'uint';
|
||
|
}
|
||
|
else if (it.base_type == 'UBMT_FLOAT32')
|
||
|
{
|
||
|
column_format = 'float';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (member_search)
|
||
|
{
|
||
|
if (!it.cpp_name.toLowerCase().includes(member_search.toLowerCase()))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const k_suffixes = ['.x', '.y', '.z', '.w'];
|
||
|
|
||
|
var relative_offset = 0;
|
||
|
for (var elem_id = 0; elem_id < Math.max(it.member['NumElements'], 1); elem_id++)
|
||
|
{
|
||
|
for (var row_id = 0; row_id < Math.max(it.member['NumRows'], 1); row_id++)
|
||
|
{
|
||
|
for (var column_id = 0; column_id < it.member['NumColumns']; column_id++)
|
||
|
{
|
||
|
var name = it.cpp_name;
|
||
|
|
||
|
if (it.member['NumElements'] > 0)
|
||
|
{
|
||
|
name += `[${elem_id}]`;
|
||
|
}
|
||
|
if (it.member['NumRows'] > 1)
|
||
|
{
|
||
|
name += k_suffixes[row_id];
|
||
|
}
|
||
|
if (it.member['NumColumns'] > 1)
|
||
|
{
|
||
|
name += k_suffixes[column_id];
|
||
|
}
|
||
|
|
||
|
member_count += 1;
|
||
|
member_byte_offset.push(it.byte_offset + relative_offset);
|
||
|
member_format.push(column_format);
|
||
|
member_names.push(name);
|
||
|
|
||
|
relative_offset += 4;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
member_count = byte_per_element / 4;
|
||
|
|
||
|
var text_format = document.getElementById('buffer_visualization_format').value;
|
||
|
|
||
|
var column_formats = text_format.split(',');
|
||
|
|
||
|
var byte_offset = 0;
|
||
|
|
||
|
column_formats.forEach(function(value, index) {
|
||
|
var format_to_use = value.trim();
|
||
|
|
||
|
member_byte_offset.push(byte_offset);
|
||
|
member_format.push(format_to_use);
|
||
|
member_names.push(format_to_use);
|
||
|
|
||
|
byte_offset += 4;
|
||
|
});
|
||
|
|
||
|
for (var i = member_format.length; i < member_count; i++)
|
||
|
{
|
||
|
var format_to_use = member_format[i % column_formats.length];
|
||
|
|
||
|
member_byte_offset.push(byte_offset);
|
||
|
member_format.push(format_to_use);
|
||
|
member_names.push(format_to_use);
|
||
|
|
||
|
byte_offset += 4;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
var column_count = member_count;
|
||
|
var row_count = num_element;
|
||
|
if (this.display_elem_using_rows)
|
||
|
{
|
||
|
if (this.structure_metadata)
|
||
|
{
|
||
|
column_count = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
column_count = 1;
|
||
|
}
|
||
|
row_count = member_count;
|
||
|
}
|
||
|
|
||
|
var display_start_row_id = Math.max(this.display_row_start - k_buffer_display_row_buffer, 0);
|
||
|
var display_row_count = Math.min(row_count - display_start_row_id, k_buffer_max_display_row_count + 2 * k_buffer_display_row_buffer);
|
||
|
var display_end_row_id = display_start_row_id + display_row_count;
|
||
|
|
||
|
var scroll_top = document.getElementById('buffer_viewport').scrollTop;
|
||
|
|
||
|
if (refresh_header)
|
||
|
{
|
||
|
var address_search_value = '';
|
||
|
if (document.getElementById('buffer_address_input'))
|
||
|
{
|
||
|
address_search_value = document.getElementById('buffer_address_input').value;
|
||
|
}
|
||
|
|
||
|
var classes = 'pretty_table';
|
||
|
if (this.display_elem_using_rows)
|
||
|
{
|
||
|
classes += ' display_elem_using_rows';
|
||
|
}
|
||
|
|
||
|
var resource_content = `
|
||
|
<table id="buffer_content_table" class="${classes}" width="100%">
|
||
|
<tr class="header" id="buffer_content_header">`;
|
||
|
|
||
|
if (this.display_elem_using_rows)
|
||
|
{
|
||
|
resource_content += `<td>Member</td><td>Value</td>`;
|
||
|
if (this.structure_metadata)
|
||
|
{
|
||
|
resource_content += `<td>Hex</td>`;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
resource_content += `<td><input type="text" id="buffer_address_input" onchange="g_view.resource_view.go_to_address();" style="width: 100%;" placeholder="Address..." value="${address_search_value}" /></td>`;
|
||
|
for (var i = 0; i < member_count; i++)
|
||
|
{
|
||
|
resource_content += `<td>${member_names[i]}</td>`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resource_content += `
|
||
|
<td></td>
|
||
|
</tr>
|
||
|
</table>`;
|
||
|
|
||
|
document.getElementById('buffer_content_pannel').innerHTML = resource_content;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var table_dom = document.getElementById("buffer_content_table");
|
||
|
while (table_dom.lastElementChild != table_dom.firstElementChild)
|
||
|
{
|
||
|
table_dom.removeChild(table_dom.lastElementChild);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
// Start padding
|
||
|
var resource_content = ``;
|
||
|
{
|
||
|
resource_content += `
|
||
|
<tr>
|
||
|
<td style="height: ${k_buffer_pixel_per_row * display_start_row_id}px; padding: 0;"></td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
|
||
|
for (var row_id = display_start_row_id; row_id < display_end_row_id; row_id++)
|
||
|
{
|
||
|
var elem_id = row_id;
|
||
|
if (this.display_elem_using_rows)
|
||
|
{
|
||
|
elem_id = 0;
|
||
|
}
|
||
|
|
||
|
var classes = '';
|
||
|
if (elem_id == this.selected_address)
|
||
|
{
|
||
|
classes = 'highlighted';
|
||
|
}
|
||
|
|
||
|
resource_content += `
|
||
|
<tr class="${classes}">`;
|
||
|
|
||
|
if (this.display_elem_using_rows)
|
||
|
{
|
||
|
resource_content += `
|
||
|
<td>${member_names[row_id]}</td>`;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
resource_content += `
|
||
|
<td>0x${(elem_id * byte_per_element).toString(16)}</td>`;
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < column_count; i++)
|
||
|
{
|
||
|
var member_id = i;
|
||
|
if (this.display_elem_using_rows)
|
||
|
{
|
||
|
member_id = row_id;
|
||
|
}
|
||
|
|
||
|
var byte_offset = byte_per_element * elem_id + member_byte_offset[member_id];
|
||
|
|
||
|
var value = null;
|
||
|
var display = member_format[member_id];
|
||
|
|
||
|
if (this.display_elem_using_rows && i == 1 && this.structure_metadata)
|
||
|
{
|
||
|
display = `hex(${display})`;
|
||
|
}
|
||
|
|
||
|
if (display == 'float')
|
||
|
{
|
||
|
value = decode_float32((new Uint32Array(this.raw_buffer_data, byte_offset, 1))[0]);
|
||
|
}
|
||
|
else if (display == 'half')
|
||
|
{
|
||
|
value = decode_float16((new Uint16Array(this.raw_buffer_data, byte_offset, 1))[0]);
|
||
|
}
|
||
|
else if (display == 'int')
|
||
|
{
|
||
|
value = (new Int32Array(this.raw_buffer_data, byte_offset, 1))[0];
|
||
|
}
|
||
|
else if (display == 'uint')
|
||
|
{
|
||
|
value = (new Uint32Array(this.raw_buffer_data, byte_offset, 1))[0];
|
||
|
}
|
||
|
else if (display == 'short')
|
||
|
{
|
||
|
value = (new Int16Array(this.raw_buffer_data, byte_offset, 1))[0];
|
||
|
}
|
||
|
else if (display == 'ushort')
|
||
|
{
|
||
|
value = (new Uint16Array(this.raw_buffer_data, byte_offset, 1))[0];
|
||
|
}
|
||
|
else if (display == 'char')
|
||
|
{
|
||
|
value = (new Int8Array(this.raw_buffer_data, byte_offset, 1))[0];
|
||
|
}
|
||
|
else if (display == 'uchar')
|
||
|
{
|
||
|
value = (new Uint8Array(this.raw_buffer_data, byte_offset, 1))[0];
|
||
|
}
|
||
|
else if (display == 'hex(int)' || display == 'hex(uint)' || display == 'hex(float)')
|
||
|
{
|
||
|
value = '0x' + (new Uint32Array(this.raw_buffer_data, byte_offset, 1))[0].toString(16).padStart(8, 0);
|
||
|
}
|
||
|
else if (display == 'hex(short)' || display == 'hex(ushort)' || display == 'hex(half)')
|
||
|
{
|
||
|
value = '0x' + (new Uint16Array(this.raw_buffer_data, byte_offset, 1))[0].toString(16).padStart(4, 0);
|
||
|
}
|
||
|
else if (display == 'hex(char)' || display == 'hex(uchar)')
|
||
|
{
|
||
|
value = '0x' + (new Uint8Array(this.raw_buffer_data, byte_offset, 1))[0].toString(16).padStart(2, 0);
|
||
|
}
|
||
|
else if (display == 'bin(int)' || display == 'bin(uint)' || display == 'bin(float)')
|
||
|
{
|
||
|
value = '0b' + (new Uint32Array(this.raw_buffer_data, byte_offset, 1))[0].toString(2).padStart(32, 0);
|
||
|
}
|
||
|
else if (display == 'bin(short)' || display == 'bin(ushort)' || display == 'bin(half)')
|
||
|
{
|
||
|
value = '0b' + (new Uint16Array(this.raw_buffer_data, byte_offset, 1))[0].toString(2).padStart(16, 0);
|
||
|
}
|
||
|
else if (display == 'bin(char)' || display == 'bin(uchar)')
|
||
|
{
|
||
|
value = '0b' + (new Uint8Array(this.raw_buffer_data, byte_offset, 1))[0].toString(2).padStart(8, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
value = `Unknown ${display}`;
|
||
|
}
|
||
|
|
||
|
resource_content += `<td>${value}</td>`;
|
||
|
}
|
||
|
|
||
|
resource_content += `
|
||
|
<td></td>
|
||
|
</tr>`;
|
||
|
} // for (var row_id = display_start_row_id; row_id < display_end_row_id; row_id++)
|
||
|
|
||
|
if (row_count == 0)
|
||
|
{
|
||
|
resource_content += `
|
||
|
<tr>
|
||
|
<td colspan="${column_count + 2}" class="empty" align="center">Empty</td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
|
||
|
// End padding
|
||
|
{
|
||
|
resource_content += `
|
||
|
<tr>
|
||
|
<td style="height: ${k_buffer_pixel_per_row * (row_count - display_end_row_id)}px; padding: 0;"></td>
|
||
|
</tr>`;
|
||
|
}
|
||
|
|
||
|
document.getElementById('buffer_content_table').innerHTML += resource_content;
|
||
|
}
|
||
|
|
||
|
document.getElementById('buffer_viewport').scrollTop = scroll_top;
|
||
|
|
||
|
{
|
||
|
var min_row_count = 10;
|
||
|
document.getElementById('buffer_viewport').style.height = `${k_scroll_width + Math.min(Math.max(row_count + 1, min_row_count), k_buffer_max_display_row_count) * k_buffer_pixel_per_row}px`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
release()
|
||
|
{
|
||
|
this.raw_buffer_data = null;
|
||
|
}
|
||
|
} // class GenericBufferView
|
||
|
|
||
|
|
||
|
class BufferView extends GenericBufferView
|
||
|
{
|
||
|
constructor(subresource_version_info, resource_desc)
|
||
|
{
|
||
|
super(subresource_version_info, resource_desc);
|
||
|
this.raw_buffer_data_path = `Resources/${get_subresource_unique_version_name(this.subresource_version_info)}.bin`;
|
||
|
|
||
|
if (resource_desc['Metadata'] != k_null_json_ptr)
|
||
|
{
|
||
|
this.structure_metadata = load_structure_metadata(resource_desc['Metadata']);
|
||
|
}
|
||
|
}
|
||
|
} // class BufferView
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- PARAMETER STRUCTURE
|
||
|
|
||
|
class ParameterStructureView extends GenericBufferView
|
||
|
{
|
||
|
constructor(subresource_version_info, resource_desc, structure_metadata)
|
||
|
{
|
||
|
super(subresource_version_info, resource_desc);
|
||
|
this.raw_buffer_data_path = `Structures/${subresource_version_info['resource']}.bin`;
|
||
|
this.structure_metadata = structure_metadata;
|
||
|
}
|
||
|
|
||
|
} // class ParameterStructureView
|
||
|
|
||
|
function display_pass_parameters(pass_id)
|
||
|
{
|
||
|
display_pass_internal(pass_id);
|
||
|
|
||
|
var pass_data = g_passes[pass_id];
|
||
|
var structure_metadata = load_structure_metadata(pass_data['ParametersMetadata']);
|
||
|
|
||
|
var subresource_version_info = {};
|
||
|
subresource_version_info['subresource'] = null;
|
||
|
subresource_version_info['resource'] = pass_data['Parameters'];
|
||
|
subresource_version_info['pass'] = pass_data['Pointer'];
|
||
|
subresource_version_info['draw'] = -1;
|
||
|
|
||
|
var resource_desc = {};
|
||
|
resource_desc['Name'] = 'PassParameters';
|
||
|
resource_desc['ByteSize'] = structure_metadata['Size'];
|
||
|
resource_desc['Desc'] = 'FRDGParameterStruct';
|
||
|
resource_desc['BytesPerElement'] = resource_desc['ByteSize'];
|
||
|
resource_desc['NumElements'] = 1;
|
||
|
resource_desc['Metadata'] = pass_data['ParametersMetadata'];
|
||
|
resource_desc['Usage'] = [];
|
||
|
|
||
|
g_view.set_resource_view(new ParameterStructureView(subresource_version_info, resource_desc, structure_metadata));
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- UI
|
||
|
|
||
|
function update_href_selection(parent)
|
||
|
{
|
||
|
var all_a = parent.getElementsByTagName("a");
|
||
|
|
||
|
var navs = [location.hash.substring(1)];
|
||
|
if (g_view)
|
||
|
{
|
||
|
navs = navs.concat(g_view.navigations);
|
||
|
}
|
||
|
|
||
|
for (let a of all_a)
|
||
|
{
|
||
|
var href = a.href.split('#');
|
||
|
|
||
|
if (navs.includes(`${href[1]}`))
|
||
|
{
|
||
|
a.classList.add('match_location_hash');
|
||
|
}
|
||
|
else if (a.classList.contains('match_location_hash'))
|
||
|
{
|
||
|
a.classList.remove('match_location_hash');
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function update_value_selection(parent, selected_value)
|
||
|
{
|
||
|
var all_a = parent.getElementsByTagName("input");
|
||
|
|
||
|
for (let a of all_a)
|
||
|
{
|
||
|
if (a.value == selected_value)
|
||
|
{
|
||
|
a.classList.add('match_value');
|
||
|
}
|
||
|
else if (a.classList.contains('match_value'))
|
||
|
{
|
||
|
a.classList.remove('match_value');
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function render_selection_list_html(parent_dom, display_list, options)
|
||
|
{
|
||
|
if ('deduplicate' in options)
|
||
|
{
|
||
|
var href_set = new Set();
|
||
|
var new_display_list = [];
|
||
|
for (const display_list_item of display_list)
|
||
|
{
|
||
|
if (display_list_item['href'])
|
||
|
{
|
||
|
if (!href_set.has(display_list_item['href']))
|
||
|
{
|
||
|
href_set.add(display_list_item['href']);
|
||
|
new_display_list.push(display_list_item);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
new_display_list.push(display_list_item);
|
||
|
}
|
||
|
}
|
||
|
display_list = new_display_list;
|
||
|
}
|
||
|
|
||
|
if ('sort' in options)
|
||
|
{
|
||
|
display_list.sort(function(a, b)
|
||
|
{
|
||
|
if (a['name'] < b['name'])
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else if (a['name'] > b['name'])
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var search = '';
|
||
|
if ('search' in options)
|
||
|
{
|
||
|
search = options['search'];
|
||
|
}
|
||
|
|
||
|
var html = '';
|
||
|
for (const display_list_item of display_list)
|
||
|
{
|
||
|
if (search && !display_list_item['name'].toLowerCase().includes(search.toLowerCase()))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (display_list_item['href'])
|
||
|
{
|
||
|
html += `<a href="${display_list_item['href']}">${display_list_item['name']}</a>`;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
html += `<a>${display_list_item['name']}</a>`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (html == '')
|
||
|
{
|
||
|
if (search)
|
||
|
{
|
||
|
html += `<div class="empty">No matches for "${search}"</div>`;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
html += `<div class="empty">None</div>`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
parent_dom.innerHTML = html;
|
||
|
update_href_selection(parent_dom);
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- WINDOW LAYOUT
|
||
|
|
||
|
function init_top()
|
||
|
{
|
||
|
document.getElementById('top_bar').innerHTML = `${g_infos['Project']} - ${g_infos['Platform']} - ${g_infos['RHI']} - ${g_infos['BuildVersion']} - ${g_infos['DumpTime']} `;
|
||
|
}
|
||
|
|
||
|
function init_top_buttons()
|
||
|
{
|
||
|
document.getElementById('top_buttons').innerHTML = `
|
||
|
<a href="#display_infos();">Infos</a>
|
||
|
<a href="#display_cvars();">CVars</a>
|
||
|
<a href="#display_log();">Log</a>`;
|
||
|
document.getElementById('top_viewer_buttons').innerHTML = `
|
||
|
<a id="console_button" href="#display_console();">Console</a>`;
|
||
|
update_console_button();
|
||
|
}
|
||
|
|
||
|
function init_page()
|
||
|
{
|
||
|
// Load the dump service information to know how to read files. This is independent of Base/Infos.json because may depends whether it was uploaded through the DumpGPUServices plugin.
|
||
|
g_dump_service = load_json('Base/DumpService.json');
|
||
|
|
||
|
// Load the base information of the page.
|
||
|
g_infos = load_json('Base/Infos.json');
|
||
|
|
||
|
if (!g_dump_service || !g_infos)
|
||
|
{
|
||
|
document.body.innerHTML = 'Please use OpenGPUDumpViewer script for the viewer to work correctly.';
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Verify the status of the dump, to check whether a crash happened during the dump.
|
||
|
{
|
||
|
var dump_status = load_text_file('Base/Status.txt');
|
||
|
if (dump_status == 'ok')
|
||
|
{
|
||
|
add_console_event('log', `The dump completed gracefully.`);
|
||
|
}
|
||
|
else if (dump_status == 'crash')
|
||
|
{
|
||
|
add_console_event('error', `A crash happened while the frame was being dumped.`);
|
||
|
}
|
||
|
else if (dump_status == 'dumping')
|
||
|
{
|
||
|
add_console_event('error', `The dump has not completed. This may be due to opening the viewer before completion, or serious problem that has not been handled with FCoreDelegates::OnShutdownAfterError`);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
add_console_event('error', `The dump completed with status: ${dump_status}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Load the cvars used for dumping.
|
||
|
{
|
||
|
g_dump_cvars = {};
|
||
|
|
||
|
var cvars_csv = load_text_file('Base/ConsoleVariables.csv');
|
||
|
var csv_lines = cvars_csv.split('\n');
|
||
|
for (var i = 1; i < csv_lines.length - 1; i++)
|
||
|
{
|
||
|
var csv_line = csv_lines[i].split(',');
|
||
|
var entry = {};
|
||
|
entry['name'] = csv_line[0];
|
||
|
if (entry['name'].startsWith('r.DumpGPU.'))
|
||
|
{
|
||
|
entry['type'] = csv_line[1];
|
||
|
entry['set_by'] = csv_line[2];
|
||
|
entry['value'] = csv_line[3];
|
||
|
g_dump_cvars[entry['name']] = entry['value'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// Load all the passes.
|
||
|
g_passes = load_json_dict_sequence('Base/Passes.json');
|
||
|
{
|
||
|
for (var pass_id = 0; pass_id < g_passes.length; pass_id++)
|
||
|
{
|
||
|
g_passes[pass_id]['DrawCount'] = 0;
|
||
|
if (!g_passes[pass_id]['EventName'])
|
||
|
{
|
||
|
g_passes[pass_id]['EventName'] = 'Unnamed pass';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var pass_draw_counts = load_json_dict_sequence('Base/PassDrawCounts.json');
|
||
|
if (pass_draw_counts)
|
||
|
{
|
||
|
pass_draw_counts.forEach(function(pass_draw_count_data) {
|
||
|
for (var pass_id = 0; pass_id < g_passes.length; pass_id++)
|
||
|
{
|
||
|
if (g_passes[pass_id]['Pointer'] == pass_draw_count_data['Pointer'])
|
||
|
{
|
||
|
g_passes[pass_id]['DrawCount'] = pass_draw_count_data['DrawCount'];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Load all the resource descriptors
|
||
|
{
|
||
|
g_descs = {};
|
||
|
|
||
|
var desc_list = load_json_dict_sequence('Base/ResourceDescs.json');
|
||
|
for (const desc of desc_list)
|
||
|
{
|
||
|
g_descs[desc['UniqueResourceName']] = desc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!does_file_exists(get_log_path()))
|
||
|
{
|
||
|
add_console_event('error', `The dump does not contain ${get_log_path()}. The log is normally copied into the dump directory once the dump is completed. Failing to have is may be due to a premature end of the dumping process.`);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
add_console_event('log', `Viewer init ok`);
|
||
|
}
|
||
|
|
||
|
init_top();
|
||
|
init_top_buttons();
|
||
|
display_pass_hierarchy();
|
||
|
|
||
|
// Find the last passes that have a displayable texture 2D.
|
||
|
if (location.hash)
|
||
|
{
|
||
|
return navigate_to_hash();
|
||
|
}
|
||
|
|
||
|
// Find the last pass that modify the RDG resource set by 'r.DumpGPU.Viewer.Visualize'
|
||
|
if ('r.DumpGPU.Viewer.Visualize' in g_dump_cvars && g_dump_cvars['r.DumpGPU.Viewer.Visualize'] != '')
|
||
|
{
|
||
|
var pass_id_to_open = -1;
|
||
|
var subresource_unique_name_to_open = null;
|
||
|
|
||
|
document.getElementById('resource_search_input').value = g_dump_cvars['r.DumpGPU.Viewer.Visualize'];
|
||
|
display_pass_hierarchy();
|
||
|
|
||
|
for (var pass_id = 0; pass_id < g_passes.length && pass_id_to_open == -1; pass_id++)
|
||
|
{
|
||
|
var pass_data = g_passes[g_passes.length - 1 - pass_id];
|
||
|
|
||
|
for (var subresource_unique_name of pass_data['OutputResources'])
|
||
|
{
|
||
|
var subresource_info = parse_subresource_unique_name(subresource_unique_name);
|
||
|
var resource_desc = get_resource_desc(subresource_info['resource']);
|
||
|
|
||
|
if (resource_desc === null)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (resource_desc['Name'] == g_dump_cvars['r.DumpGPU.Viewer.Visualize'] && pass_id_to_open == -1)
|
||
|
{
|
||
|
pass_id_to_open = g_passes.length - 1 - pass_id;
|
||
|
subresource_unique_name_to_open = subresource_unique_name;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pass_id_to_open != -1)
|
||
|
{
|
||
|
return redirect_to_hash(`display_output_resource(${pass_id_to_open},'${subresource_unique_name_to_open}');`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return display_tip();
|
||
|
}
|
||
|
|
||
|
function onresize_body()
|
||
|
{
|
||
|
var w = window.innerWidth;
|
||
|
var h = window.innerHeight;
|
||
|
|
||
|
var top_h = 40;
|
||
|
|
||
|
document.getElementById('onresize_body_top_bar').style.width = `${w}px`;
|
||
|
document.getElementById('onresize_body_top_bar').style.height = `${top_h}px`;
|
||
|
|
||
|
document.getElementById('onresize_body_left').style.width = `${Math.floor(w * 0.25)}px`;
|
||
|
document.getElementById('onresize_body_left').style.height = `${h - top_h}px`;
|
||
|
|
||
|
document.getElementById('onresize_body_right').style.width = `${w - Math.floor(w * 0.25)}px`;
|
||
|
document.getElementById('onresize_body_right').style.height = `${h - top_h}px`;
|
||
|
|
||
|
document.getElementById('onresize_body_right').style.width = `${w - Math.floor(w * 0.25)}px`;
|
||
|
document.getElementById('onresize_body_right').style.height = `${h - top_h}px`;
|
||
|
|
||
|
{
|
||
|
var search_h = (30 + 2 * 5) * 2;
|
||
|
document.getElementById('pass_hierarchy').style.width = `${Math.floor(w * 0.25) - 10 * 2}px`;
|
||
|
document.getElementById('pass_hierarchy').style.height = `${h - top_h - search_h - 10 * 2}px`;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
//document.getElementById('main_right_pannel').style.width = document.getElementById('onresize_body_right').style.width;
|
||
|
document.getElementById('main_right_pannel').style.height = `${h - top_h - 10 * 2}px`;
|
||
|
}
|
||
|
|
||
|
if (g_view !== null)
|
||
|
{
|
||
|
var ctx = {};
|
||
|
ctx.width = w - Math.floor(w * 0.25);
|
||
|
g_view.resize(ctx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------- NAVIGATION
|
||
|
|
||
|
var g_previous_location_hash = null;
|
||
|
|
||
|
function redirect_to_hash(new_hash)
|
||
|
{
|
||
|
if (new_hash === g_previous_location_hash)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
location.hash = new_hash;
|
||
|
}
|
||
|
|
||
|
function navigate_to_hash()
|
||
|
{
|
||
|
if (location.hash === g_previous_location_hash)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
g_previous_location_hash = location.hash;
|
||
|
|
||
|
if (location.hash)
|
||
|
{
|
||
|
var js = location.hash.substring(1);
|
||
|
eval(js);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
display_tip();
|
||
|
}
|
||
|
|
||
|
update_href_selection(document);
|
||
|
}
|
||
|
|
||
|
function get_current_navigation()
|
||
|
{
|
||
|
return g_previous_location_hash;
|
||
|
}
|
||
|
|
||
|
</script>
|
||
|
|
||
|
|
||
|
<style type="text/css">
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- them colors */
|
||
|
|
||
|
:root
|
||
|
{
|
||
|
--border-radius: 2px;
|
||
|
|
||
|
--body-bg-color: rgb(21, 21, 21);
|
||
|
--body-txt-size: 12px;
|
||
|
|
||
|
--div-bg-color: rgb(36, 36, 36);
|
||
|
--div-txt-color: rgb(192, 192, 192);
|
||
|
--a-bg-color-odd: rgb(26, 26, 26);
|
||
|
--a-bg-color-hover: #332222;
|
||
|
--a-bg-color-hoverhref: rgb(38, 67, 81);
|
||
|
--a-txt-color-disabled: rgb(113, 113, 113);
|
||
|
|
||
|
--a-bg-color-selected: rgb(38, 187, 255);
|
||
|
--a-txt-color-selected: rgb(15, 15, 15);
|
||
|
|
||
|
--channel-red-color: rgb(255, 38, 38);
|
||
|
--channel-green-color: rgb(38, 255, 38);
|
||
|
--channel-blue-color: rgb(38, 187, 255);
|
||
|
--channel-alpha-color: white;
|
||
|
|
||
|
--error-txt-color: var(--channel-red-color);
|
||
|
|
||
|
--header-bg-color: rgb(47, 47, 47);
|
||
|
|
||
|
--button-border-radius: var(--border-radius);
|
||
|
--button-bg-color: rgb(56, 56, 56);
|
||
|
--button-bg-color-hover: rgb(87, 87, 87);
|
||
|
--button-bg-color-selected: rgb(15, 15, 15);
|
||
|
--button-txt-color: var(--div-txt-color);
|
||
|
--button-txt-color-hover: rgb(255, 255, 255);
|
||
|
--button-txt-color-selected: rgb(38, 187, 255);
|
||
|
|
||
|
|
||
|
--scroll-bg-color: var(--a-bg-color-odd);
|
||
|
--scroll-color: rgb(87, 87, 87);
|
||
|
--scroll-color-hover: rgb(128, 128, 128);
|
||
|
|
||
|
--input-bg-color: rgb(15, 15, 15);
|
||
|
--input-border: 1px solid rgb(55, 55, 55);
|
||
|
--input-border-hover: rgb(83, 83, 83);
|
||
|
--input-border-focus: rgb(38, 176, 239);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- override default */
|
||
|
|
||
|
body
|
||
|
{
|
||
|
overflow: hidden;
|
||
|
background: var(--body-bg-color);
|
||
|
color: var(--div-txt-color);
|
||
|
margin: 0;
|
||
|
padding: 0;
|
||
|
/*font-family: Arial, Helvetica, sans-serif;*/
|
||
|
font-family: consolas, "Liberation Mono", courier, monospace;
|
||
|
font-size: var(--body-txt-size);
|
||
|
/* overflow: hidden; */
|
||
|
}
|
||
|
|
||
|
table, tr, td, div, a
|
||
|
{
|
||
|
margin: 0;
|
||
|
padding: 0;
|
||
|
border-spacing: 0;
|
||
|
border-collapse: collapse;
|
||
|
vertical-align: top;
|
||
|
cursor: default;
|
||
|
font-size: inherit;
|
||
|
}
|
||
|
|
||
|
td
|
||
|
{
|
||
|
cursor: default;
|
||
|
}
|
||
|
|
||
|
div, a
|
||
|
{
|
||
|
display: block;
|
||
|
cursor: default;
|
||
|
}
|
||
|
|
||
|
a, a:hover, a:visited
|
||
|
{
|
||
|
color: inherit;
|
||
|
font: inherit;
|
||
|
text-decoration: none;
|
||
|
cursor: default;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- external link */
|
||
|
|
||
|
a.external_link
|
||
|
{
|
||
|
color: inherit;
|
||
|
cursor: pointer;
|
||
|
}
|
||
|
|
||
|
a.external_link:hover
|
||
|
{
|
||
|
color: var(--button-txt-color-hover);
|
||
|
}
|
||
|
|
||
|
a.external_link:active
|
||
|
{
|
||
|
color: var(--button-txt-color-selected);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- inputs scroll bars */
|
||
|
|
||
|
input
|
||
|
{
|
||
|
padding: 3px 5px;
|
||
|
border-radius: var(--border-radius);
|
||
|
}
|
||
|
|
||
|
input[type=search]
|
||
|
{
|
||
|
padding: 3px 13px;
|
||
|
border-radius: 20px;
|
||
|
}
|
||
|
|
||
|
textarea
|
||
|
{
|
||
|
padding: 3px 3px;
|
||
|
}
|
||
|
|
||
|
input, textarea
|
||
|
{
|
||
|
background-color: var(--input-bg-color);
|
||
|
color: var(--div-txt-color);
|
||
|
border: var(--input-border);
|
||
|
outline: none;
|
||
|
font-size: inherit;
|
||
|
font: inherit;
|
||
|
}
|
||
|
|
||
|
input[readonly], textarea[readonly]
|
||
|
{
|
||
|
color: var(--a-txt-color-disabled);
|
||
|
}
|
||
|
|
||
|
input:not([readonly]):hover,
|
||
|
textarea:not([readonly]):hover
|
||
|
{
|
||
|
border-color: var(--input-border-hover);
|
||
|
}
|
||
|
|
||
|
input[type=text]:not([readonly]):focus,
|
||
|
input[type=search]:not([readonly]):focus,
|
||
|
textarea:not([readonly]):focus
|
||
|
{
|
||
|
color: rgb(254, 254, 254);
|
||
|
border-color: var(--input-border-focus);
|
||
|
}
|
||
|
|
||
|
input:focus, textarea:focus
|
||
|
{
|
||
|
outline: none;
|
||
|
}
|
||
|
|
||
|
input::placeholder
|
||
|
{
|
||
|
color: rgb(76, 76, 76);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- webkit scroll bars */
|
||
|
|
||
|
*::-webkit-scrollbar {
|
||
|
width: 8px;
|
||
|
height: 8px;
|
||
|
background: var(--scroll-bg-color);
|
||
|
}
|
||
|
|
||
|
*::-webkit-scrollbar-corner, *::-webkit-scrollbar-track {
|
||
|
background: var(--scroll-bg-color);
|
||
|
}
|
||
|
|
||
|
*::-webkit-scrollbar-thumb {
|
||
|
background-color: var(--scroll-color);
|
||
|
border-radius: 20px;
|
||
|
}
|
||
|
|
||
|
*::-webkit-scrollbar-thumb:hover {
|
||
|
background-color: var(--scroll-color-hover);
|
||
|
border-radius: 20px;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- common layout */
|
||
|
|
||
|
.main_div
|
||
|
{
|
||
|
background: var(--div-bg-color);
|
||
|
padding: 5px;
|
||
|
margin: 5px;
|
||
|
border-radius: 3px;
|
||
|
}
|
||
|
|
||
|
#main_right_pannel .main_div
|
||
|
{
|
||
|
margin-bottom: 20px;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- Selection list */
|
||
|
|
||
|
.selection_list_title
|
||
|
{
|
||
|
font-size: 20;
|
||
|
font-weight: bold;
|
||
|
padding: 5px 20px 10px 20px;
|
||
|
}
|
||
|
|
||
|
.selection_list_search
|
||
|
{
|
||
|
padding: 0px 5px;
|
||
|
height: 30px;
|
||
|
width: auto;
|
||
|
}
|
||
|
|
||
|
.selection_list_search input[type=search]
|
||
|
{
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
.selection_list
|
||
|
{
|
||
|
max-height: inherit;
|
||
|
overflow-x: auto;
|
||
|
overflow-y: scroll;
|
||
|
}
|
||
|
|
||
|
.selection_list a
|
||
|
{
|
||
|
width: auto;
|
||
|
padding: 5px 20px;
|
||
|
white-space: nowrap;
|
||
|
}
|
||
|
|
||
|
.selection_list a:nth-child(odd)
|
||
|
{
|
||
|
background: var(--a-bg-color-odd);
|
||
|
}
|
||
|
|
||
|
.selection_list a:not(.match_location_hash):hover
|
||
|
{
|
||
|
background: var(--a-bg-color-hover);
|
||
|
}
|
||
|
|
||
|
.selection_list a:not(.match_location_hash)[href]:hover
|
||
|
{
|
||
|
background: var(--a-bg-color-hoverhref);
|
||
|
color: var(--button-txt-color-hover);
|
||
|
cursor: pointer;
|
||
|
}
|
||
|
|
||
|
/*.selection_list a.match_location_hash
|
||
|
{
|
||
|
background: var(--a-bg-color-selected);
|
||
|
color: var(--a-txt-color-selected);
|
||
|
}*/
|
||
|
|
||
|
.selection_list a.match_location_hash
|
||
|
{
|
||
|
background: var(--button-bg-color-selected);
|
||
|
color: var(--button-txt-color-selected);
|
||
|
}
|
||
|
|
||
|
.selection_list a.disabled
|
||
|
{
|
||
|
color: var(--a-txt-color-disabled);
|
||
|
}
|
||
|
|
||
|
.selection_list div.empty
|
||
|
{
|
||
|
width: 100%;
|
||
|
margin: 20px 0 0 0;
|
||
|
color: var(--a-txt-color-disabled);
|
||
|
text-align: center;
|
||
|
}
|
||
|
|
||
|
#main_right_pannel .selection_list
|
||
|
{
|
||
|
min-height: 100px;
|
||
|
max-height: 200px;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- table */
|
||
|
|
||
|
.pretty_table tr.header
|
||
|
{
|
||
|
background: var(--header-bg-color);
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
|
||
|
.pretty_table tr:not(.header):nth-child(odd) td
|
||
|
{
|
||
|
background: var(--a-bg-color-odd);
|
||
|
}
|
||
|
|
||
|
.pretty_table tr:not(.header):hover td:not(.empty)
|
||
|
{
|
||
|
background: var(--a-bg-color-hover);
|
||
|
}
|
||
|
|
||
|
.pretty_table tr td
|
||
|
{
|
||
|
display: table-cell;
|
||
|
padding: 5px 20px;
|
||
|
}
|
||
|
|
||
|
.pretty_table tr td:first-child
|
||
|
{
|
||
|
width: 150px;
|
||
|
}
|
||
|
|
||
|
.pretty_table tr.header td
|
||
|
{
|
||
|
padding: 5px 30px;
|
||
|
}
|
||
|
|
||
|
.pretty_table tr.header td:not(:first-child)
|
||
|
{
|
||
|
border-left: 1px solid rgb(36, 36, 36);
|
||
|
}
|
||
|
|
||
|
.pretty_table .empty
|
||
|
{
|
||
|
padding: 20px 0 0 0;
|
||
|
color: var(--a-txt-color-disabled);
|
||
|
text-align: center;
|
||
|
}
|
||
|
|
||
|
.pretty_table tr.error td
|
||
|
{
|
||
|
color: var(--error-txt-color);
|
||
|
}
|
||
|
|
||
|
.resource_desc tr td:not(.empty)
|
||
|
{
|
||
|
width: 150px;
|
||
|
text-align: right;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- button */
|
||
|
|
||
|
.button_list a,
|
||
|
.button_list input
|
||
|
{
|
||
|
margin: 0;
|
||
|
display: inline-block;
|
||
|
display: table-cell;
|
||
|
padding: 3px 10px;
|
||
|
background-color: var(--button-bg-color);
|
||
|
color: var(--button-txt-color);
|
||
|
cursor: pointer;
|
||
|
border-radius: 0;
|
||
|
}
|
||
|
|
||
|
.button_list a:first-child,
|
||
|
.button_list input:first-child
|
||
|
{
|
||
|
border-top-left-radius: var(--button-border-radius);
|
||
|
border-bottom-left-radius: var(--button-border-radius);
|
||
|
}
|
||
|
|
||
|
.button_list a:last-child,
|
||
|
.button_list input:last-child
|
||
|
{
|
||
|
border-top-right-radius: var(--button-border-radius);
|
||
|
border-bottom-right-radius: var(--button-border-radius);
|
||
|
}
|
||
|
|
||
|
.button_list a:not(.match_location_hash):not(:active):hover,
|
||
|
.button_list input:not(.match_value):not(:active):hover
|
||
|
{
|
||
|
background-color: var(--button-bg-color-hover);
|
||
|
}
|
||
|
|
||
|
.button_list a:not(.match_location_hash):not(.error):not(:active):hover,
|
||
|
.button_list input:not(.match_value):not(:active):hover
|
||
|
{
|
||
|
color: var(--button-txt-color-hover);
|
||
|
}
|
||
|
|
||
|
.button_list a:active,
|
||
|
.button_list a.match_location_hash,
|
||
|
.button_list input:active,
|
||
|
.button_list input.match_value
|
||
|
{
|
||
|
background-color: var(--button-bg-color-selected);
|
||
|
color: var(--button-txt-color-selected);
|
||
|
}
|
||
|
|
||
|
.button_list a.error,
|
||
|
.button_list a.error:active
|
||
|
{
|
||
|
color: var(--error-txt-color);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- top bar */
|
||
|
|
||
|
#top_bar
|
||
|
{
|
||
|
font-size: 20;
|
||
|
font-weight: bold;
|
||
|
padding: 10px;
|
||
|
display: inline-block;
|
||
|
}
|
||
|
|
||
|
#top_buttons,
|
||
|
#top_viewer_buttons
|
||
|
{
|
||
|
padding: 12px;
|
||
|
display: inline-block;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- main_right_pannel */
|
||
|
|
||
|
#main_right_pannel
|
||
|
{
|
||
|
width: auto;
|
||
|
overflow-x: auto;
|
||
|
overflow-y: scroll;
|
||
|
}
|
||
|
|
||
|
#main_right_pannel .pass_title
|
||
|
{
|
||
|
font-size: 20;
|
||
|
font-weight: bolder;
|
||
|
padding: 10px 40px;
|
||
|
}
|
||
|
|
||
|
#main_right_pannel .pass_title .button_list
|
||
|
{
|
||
|
font-size: var(--body-txt-size);
|
||
|
margin-top: 3px;
|
||
|
}
|
||
|
|
||
|
#cvars_pannel
|
||
|
{
|
||
|
height: auto;
|
||
|
max-height: none;
|
||
|
}
|
||
|
|
||
|
#cvars_pannel .pretty_table tr td:nth-child(2)
|
||
|
{
|
||
|
text-align: right;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- TextureView */
|
||
|
|
||
|
#canvas_outter
|
||
|
{
|
||
|
background-color: var(--input-bg-color);
|
||
|
color: var(--div-txt-color);
|
||
|
border: var(--input-border);
|
||
|
}
|
||
|
|
||
|
#canvas_outter:not(.selected):hover
|
||
|
{
|
||
|
border-color: var(--input-border-hover);
|
||
|
}
|
||
|
|
||
|
#canvas_outter.selected
|
||
|
{
|
||
|
border-color: var(--input-border-focus);
|
||
|
}
|
||
|
|
||
|
#canvas_outter #canvas_header
|
||
|
{
|
||
|
padding: 3px;
|
||
|
border-bottom: var(--input-border);
|
||
|
}
|
||
|
|
||
|
#canvas_outter #canvas_header .button_list
|
||
|
{
|
||
|
display: inline-block;
|
||
|
}
|
||
|
|
||
|
#canvas_outter #canvas_footer
|
||
|
{
|
||
|
padding: 3px 10px;
|
||
|
border-top: var(--input-border);
|
||
|
}
|
||
|
|
||
|
#canvas_outter #canvas_viewport
|
||
|
{
|
||
|
overflow: hidden;
|
||
|
background-image:
|
||
|
linear-gradient(45deg, #000 25%, transparent 25%),
|
||
|
linear-gradient(45deg, transparent 75%, #000 75%),
|
||
|
linear-gradient(45deg, transparent 75%, #000 75%),
|
||
|
linear-gradient(45deg, #000 25%, var(--input-bg-color) 25%);
|
||
|
background-size:16px 16px;
|
||
|
background-position:0 0, 0 0, -8px -8px, 8px 8px;
|
||
|
}
|
||
|
|
||
|
#canvas_outter .error_msg
|
||
|
{
|
||
|
width: 100%;
|
||
|
height: 100%;
|
||
|
text-align: center;
|
||
|
color: var(--error-txt-color);
|
||
|
}
|
||
|
|
||
|
#canvas_outter canvas
|
||
|
{
|
||
|
cursor: crosshair;
|
||
|
image-rendering: optimizeSpeed;
|
||
|
image-rendering: -moz-crisp-edges;
|
||
|
image-rendering: -webkit-optimize-contrast;
|
||
|
image-rendering: -o-crisp-edges;
|
||
|
image-rendering: pixelated;
|
||
|
-ms-interpolation-mode: nearest-neighbor;
|
||
|
}
|
||
|
|
||
|
#texture_visualization_code_input
|
||
|
{
|
||
|
min-width: 800px;
|
||
|
min-height: 200px;
|
||
|
display: block;
|
||
|
width: auto;
|
||
|
}
|
||
|
|
||
|
#texture_visualization_code_log
|
||
|
{
|
||
|
margin-top: 10px;
|
||
|
min-width: 800px;
|
||
|
min-height: 200px;
|
||
|
display: block;
|
||
|
width: auto;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----------------------------------------------------- BufferView */
|
||
|
|
||
|
#buffer_outter
|
||
|
{
|
||
|
background-color: var(--input-bg-color);
|
||
|
color: var(--div-txt-color);
|
||
|
border: var(--input-border);
|
||
|
}
|
||
|
|
||
|
#buffer_outter:not(.selected):hover
|
||
|
{
|
||
|
border-color: var(--input-border-hover);
|
||
|
}
|
||
|
|
||
|
#buffer_outter.selected
|
||
|
{
|
||
|
border-color: var(--input-border-focus);
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport_header
|
||
|
{
|
||
|
padding: 3px;
|
||
|
border-bottom: var(--input-border);
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport
|
||
|
{
|
||
|
overflow: hidden;
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport #buffer_content_table tr.header
|
||
|
{
|
||
|
position: sticky;
|
||
|
top: 0;
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport #buffer_content_table:not(.display_elem_using_rows) tr.header td:first-child
|
||
|
{
|
||
|
padding: 0 3px;
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport #buffer_content_table tr td:not(.empty)
|
||
|
{
|
||
|
padding: 3px 5px 0px 5px;
|
||
|
width: 60px;
|
||
|
height: 20px;
|
||
|
overflow: hidden;
|
||
|
text-align: right;
|
||
|
/*font-family: consolas, "Liberation Mono", courier, monospace;*/
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport #buffer_content_table.display_elem_using_rows tr td:not(.empty):first-child
|
||
|
{
|
||
|
text-align: left;
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport #buffer_content_table tr td:first-child
|
||
|
{
|
||
|
width: 100px;
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport #buffer_content_table tr td:last-child
|
||
|
{
|
||
|
width: auto;
|
||
|
}
|
||
|
|
||
|
#buffer_outter #buffer_viewport #buffer_content_table tr.highlighted
|
||
|
{
|
||
|
color: var(--button-txt-color-selected);
|
||
|
}
|
||
|
|
||
|
</style>
|
||
|
|
||
|
|
||
|
<body onload="init_console(); onresize_body(); init_page();" onresize="onresize_body();" onhashchange="navigate_to_hash();">
|
||
|
<table cellpadding="0" cellspacing="0" border="0" id="onresize_body_table">
|
||
|
<tr>
|
||
|
<td colspan="2" id="onresize_body_top_bar">
|
||
|
<div id="top_bar"></div>
|
||
|
<div id="top_buttons" class="button_list"></div>
|
||
|
<div id="top_viewer_buttons" class="button_list"></div>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td valign="top" id="onresize_body_left">
|
||
|
<div class="main_div">
|
||
|
<div id="pass_hierarchy_search" style="padding: 5px; height: 70px;">
|
||
|
<input type="search" id="pass_search_input" oninput="display_pass_hierarchy();" onchange="display_pass_hierarchy();" style="width: 100%;" placeholder="Search pass..." />
|
||
|
<input type="search" id="resource_search_input" oninput="display_pass_hierarchy();" onchange="display_pass_hierarchy();" style="width: 100%; margin-top: 10px;" placeholder="Search resource..." />
|
||
|
</div>
|
||
|
<div id="pass_hierarchy" class="selection_list"></div>
|
||
|
</div>
|
||
|
</td>
|
||
|
<td valign="top" id="onresize_body_right">
|
||
|
<div id="main_right_pannel"></div>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
</body>
|
||
|
</html>
|