jetbrains/renderdoc-service/src/RenderDocTexturePreviewService.cpp (163 lines of code) (raw):
#include "RenderDocTexturePreviewService.h"
#include "RenderDocCaptureContext.h"
#include "RenderDocModel/RdcPixelStageInOutputs.Generated.h"
#include "RenderDocModel/RdcWindowOutputData.Generated.h"
#include "util/ArrayUtils.h"
#include "util/RenderDocActionHelpers.h"
namespace jetbrains::renderdoc {
RenderDocTexturePreviewService::Dimensions::Dimensions(int32_t width, int32_t height) : width(width), height(height) {}
RenderDocTexturePreviewService::RenderDocTexturePreviewService(IReplayController *controller, const std::shared_ptr<RenderDocCaptureContext> &capture_context) :controller(controller), capture_context(capture_context) {
}
Descriptor RenderDocTexturePreviewService::create_descriptor(ResourceId id, Subresource sub = Subresource()) {
Descriptor descriptor;
descriptor.type = DescriptorType::ReadWriteImage;
descriptor.resource = id;
descriptor.firstMip = sub.mip;
descriptor.firstSlice = sub.slice;
return descriptor;
}
std::vector<std::pair<Descriptor, RenderDocTexturePreviewService::Dimensions>> RenderDocTexturePreviewService::calculate_dimensions(
const rdcarray<TextureDescription> &textures,
const rdcarray<Descriptor> &targets,
const TextureCategory category,
Dimensions &max_dim)
{
auto &width = max_dim.width;
auto &height = max_dim.height;
std::vector<std::pair<Descriptor, Dimensions>> descriptors;
for (auto target : targets) {
const auto texture = std::find_if(textures.begin(), textures.end(), [id = target.resource](const TextureDescription &t) { return t.resourceId == id; });
if (texture == textures.end() || !(texture->creationFlags & category))
continue;
const Dimensions dims(static_cast<int32_t>(texture->width), static_cast<int32_t>(texture->height));
width = std::max(dims.width, width);
height = std::max(dims.height, height);
descriptors.emplace_back(target, dims);
}
return descriptors;
}
void RenderDocTexturePreviewService::calculate_dimensions(const ActionDescription *action, uint32_t event_id) {
const auto textures = controller->GetTextures();
Dimensions max_dim(0, 0);
const auto input_targets = calculate_dimensions(textures, get_input_targets(action), TextureCategory::ShaderRead, max_dim);
inputs_cache[event_id].insert(inputs_cache[event_id].end(), input_targets.begin(), input_targets.end());
const auto color_output_targets = calculate_dimensions(textures, get_output_targets(action), TextureCategory::ColorTarget, max_dim);
color_outputs_cache[event_id].insert(color_outputs_cache[event_id].end(), color_output_targets.begin(), color_output_targets.end());
if (const auto depth_output_targets = calculate_dimensions(textures, {get_depth_target(action)}, TextureCategory::DepthTarget, max_dim); !depth_output_targets.empty())
depth_outputs_cache.try_emplace(event_id, depth_output_targets[0]);
max_dimensions[event_id] = max_dim;
}
void RenderDocTexturePreviewService::calculate_action_context(const ActionDescription *action, bool ©, bool &clear, bool &compute) const {
copy = action && action->flags & (ActionFlags::Copy | ActionFlags::Resolve | ActionFlags::Present);
clear = action && action->flags & ActionFlags::Clear;
compute = action && action->flags & ActionFlags::Dispatch && controller->GetPipelineState().GetShader(ShaderStage::Compute) != ResourceId();
}
void RenderDocTexturePreviewService::collect_read_only_resources(const ActionDescription *action, ShaderStage stage, rdcarray<Descriptor> &descriptors) const {
bool copy, clear, compute;
calculate_action_context(action, copy, clear, compute);
if (action && clear)
return;
if (action && copy && stage == ShaderStage::Pixel) {
descriptors.push_back(create_descriptor(action->copySource, action->copySourceSubresource));
return;
}
if (compute && stage != ShaderStage::Pixel && stage != ShaderStage::Compute)
return;
const auto &inputs = controller->GetPipelineState().GetReadOnlyResources(compute ? ShaderStage::Compute : stage, true);
for (const auto &input : inputs) {
descriptors.emplace_back(input.descriptor);
}
}
rdcarray<Descriptor> RenderDocTexturePreviewService::get_input_targets(const ActionDescription *action) const {
static constexpr ShaderStage stages[6] = { ShaderStage::Vertex, ShaderStage::Hull, ShaderStage::Domain, ShaderStage::Geometry, ShaderStage::Pixel, ShaderStage::Compute };
rdcarray<Descriptor> descriptors;
for (const auto stage : stages) {
collect_read_only_resources(action, stage, descriptors);
}
return descriptors;
}
rdcarray<Descriptor> RenderDocTexturePreviewService::get_output_targets(const ActionDescription *action) const {
bool copy, clear, compute;
calculate_action_context(action, copy, clear, compute);
if (action && (copy || clear))
return {create_descriptor(action->copyDestination)};
if (compute)
return {};
const rdcarray<Descriptor> outputs = controller->GetPipelineState().GetOutputTargets();
if (action && outputs.isEmpty() && action->flags & ActionFlags::Present) {
if(action->copyDestination != ResourceId())
return {create_descriptor(action->copyDestination)};
for(const TextureDescription &tex : controller->GetTextures())
{
if(tex.creationFlags & TextureCategory::SwapBuffer)
return {create_descriptor(tex.resourceId)};
}
}
return outputs;
}
Descriptor RenderDocTexturePreviewService::get_depth_target(const ActionDescription *action) const {
bool copy, clear, compute;
calculate_action_context(action, copy, clear, compute);
if(copy || clear || compute)
return {};
return controller->GetPipelineState().GetDepthTarget();
}
std::wstring RenderDocTexturePreviewService::get_texture_name(const ActionDescription *action, const Descriptor &desc) const {
bool copy, clear, compute;
calculate_action_context(action, copy, clear, compute);
const std::wstring bind_name = (copy || clear) ? L"Destination" : L"";
if (desc.resource == ResourceId::Null())
return L"";
std::wstringstream name_stream(bind_name);
if (!capture_context->has_auto_generated_name(desc.resource)) {
if (name_stream.tellp() != 0) {
name_stream << " = ";
}
name_stream << capture_context->get_resource_name(desc.resource);
}
if (name_stream.tellp() == 0) {
name_stream << capture_context->get_resource_name(desc.resource);
}
return name_stream.str();
}
rd::Wrapper<model::RdcPixelStageInOutputs> RenderDocTexturePreviewService::get_inoutputs(const ActionDescription *action, uint32_t event_id) {
if (max_dimensions.find(event_id) == max_dimensions.end()) {
calculate_dimensions(action, event_id);
}
if (max_dimensions[event_id].width == 0 && max_dimensions[event_id].height == 0)
return rd::Wrapper<model::RdcPixelStageInOutputs>(nullptr);
const auto max_width = max_dimensions.at(event_id).width;
const auto max_height = max_dimensions.at(event_id).height;
const auto window_data = CreateHeadlessWindowingData(max_width, max_height);
const auto &output = controller->CreateOutput(window_data, ReplayOutputType::Texture);
std::vector<rd::Wrapper<model::RdcWindowOutputData>> inputs;
if (const auto &inputs_it = inputs_cache.find(event_id); inputs_it != inputs_cache.end()) {
const auto &event_inputs = inputs_it->second;
inputs.reserve(event_inputs.size());
for (const auto &[desc, dim] : event_inputs) {
const auto &name = get_texture_name(action, desc);
const std::vector<uint8_t> buffer = ArrayUtils::CopyToVector(output->DrawThumbnail(dim.width, dim.height, desc.resource, {}, CompType::Typeless));
inputs.emplace_back(rd::wrapper::make_wrapper<model::RdcWindowOutputData>(name, dim.width, dim.height, buffer));
}
}
std::vector<rd::Wrapper<model::RdcWindowOutputData>> color_outs;
if (const auto &outputs_it = color_outputs_cache.find(event_id); outputs_it != color_outputs_cache.end()) {
const auto &event_outputs = outputs_it->second;
color_outs.reserve(event_outputs.size());
for (const auto &[desc, dim] : event_outputs) {
const auto &name = get_texture_name(action, desc);
const std::vector<uint8_t> buffer = ArrayUtils::CopyToVector(output->DrawThumbnail(dim.width, dim.height, desc.resource, {}, CompType::Typeless));
color_outs.emplace_back(rd::wrapper::make_wrapper<model::RdcWindowOutputData>(name, dim.width, dim.height, buffer));
}
}
rd::Wrapper<model::RdcWindowOutputData> depth_out(nullptr);
if (const auto &depth_it = depth_outputs_cache.find(event_id); depth_it != depth_outputs_cache.end()) {
const auto &[desc, dim] = depth_it->second;
const auto &name = get_texture_name(action, desc);
const std::vector<uint8_t> buffer = ArrayUtils::CopyToVector(output->DrawThumbnail(dim.width, dim.height, desc.resource, {}, CompType::Typeless));
depth_out = rd::wrapper::make_wrapper<model::RdcWindowOutputData>(name, dim.width, dim.height, buffer);
}
output->Shutdown();
return rd::wrapper::make_wrapper<model::RdcPixelStageInOutputs>(inputs, color_outs, depth_out);
}
}