Files
dhkatzandClaude Opus 5.5 6e4fe589e5
CI / build (push) Successful in 48s
Draw render targets in the renderer process
While the renderer process runs, the adapter makes the client's render targets
itself and the renderer process draws frames into them: texture targets are
drawn with like any texture, and a pixmap target's frame is read back through
the channel into its Pixmap (protocol version 4: BeginFrame names its target,
CreateRenderTarget, Channel::readSize and the -read event). Frames into render
targets are drawn even while the window is hidden.

The client uses pixmap targets for snapshots (outfit pictures, photos), checked
to look right. The render target interfaces are declared from IMVU's vtables.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:09:48 -07:00

152 lines
6.4 KiB
C++

#pragma once
// Carries out the command stream (include/t5de/protocol/commands.hpp) with Diligent, reproducing IMVU's Direct3D 9
// look (shaders/fixed_function.hlsl): the client's textures and buffers by id, its state as it sets it, a pipeline
// for each combination of states it draws with.
#include <cstddef>
#include <cstdint>
#include <functional>
#include <span>
#include <string>
#include <unordered_map>
#include <vector>
#include "EngineFactoryD3D11.h"
#include "RefCntAutoPtr.hpp"
#include "t5de/protocol/commands.hpp"
namespace t5de {
class Scene {
public:
Scene(Diligent::IRenderDevice* device, Diligent::IDeviceContext* context, Diligent::ISwapChain* swapChain);
// Carries out one frame's commands. offsetX is where the window starts within the client's 3D view; without draw,
// only the frame's changes are made (textures, buffers, state) and frames into the window aren't drawn (frames into
// render targets always are). True if the frame was drawn into the window (to present).
bool execute(std::span<const std::byte> commands, int offsetX, bool draw);
// Receives the pixels of frames that read their render target back (protocol::BeginFrame::readBack): rows packed,
// 0xAARRGGBB; empty if they couldn't be read. Valid during the call.
void setReadBack(std::function<void(std::span<const std::byte>)> readBack) { readBack_ = std::move(readBack); }
// Replaces the shaders' source (the built-in one by default): for trying changes while the client runs.
void setShaderSource(std::string source);
// When the adapter sent the last frame carried out (QueryPerformanceCounter).
[[nodiscard]] std::uint64_t lastSentAt() const { return lastSentAt_; }
private:
struct Texture {
Diligent::RefCntAutoPtr<Diligent::ITexture> texture;
Diligent::RefCntAutoPtr<Diligent::ITexture> depth; // render targets only
std::uint32_t width = 0;
std::uint32_t height = 0;
std::uint32_t flags = 0;
};
struct Buffer {
Diligent::RefCntAutoPtr<Diligent::IBuffer> buffer;
protocol::BufferKind kind = protocol::BufferKind::Vertex;
std::uint32_t elementSize = 0;
std::uint32_t count = 0;
};
struct Pipeline {
Diligent::RefCntAutoPtr<Diligent::IPipelineState> state;
Diligent::RefCntAutoPtr<Diligent::IShaderResourceBinding> bindings;
Diligent::IShaderResourceVariable* texture0 = nullptr;
Diligent::IShaderResourceVariable* texture1 = nullptr;
};
struct Shaders {
Diligent::RefCntAutoPtr<Diligent::IShader> vertex;
Diligent::RefCntAutoPtr<Diligent::IShader> pixel;
};
// Mirrors the shader's constant buffer.
struct Constants {
float world[16];
float view[16];
float projection[16];
float texTransform0[8];
float texTransform1[8];
float ambient[4];
float lightColor[8][4];
float lightVector[8][4];
float fogColor[4];
float fog[4];
float flags[4];
float viewport[4];
};
// What a draw needs from the state, as set by the client.
struct State {
bool depthTest = true;
bool depthWrite = true;
bool blending = false;
std::uint32_t blendMode = 1;
bool culling = true;
bool normalInverting = false;
std::uint32_t texture0 = 0;
std::uint32_t texture1 = 0;
};
void createTexture(const protocol::Command& command);
void createRenderTarget(const protocol::CreateRenderTarget& create);
void beginFrame(const protocol::BeginFrame& begin);
void endFrame();
// Sets the viewport for the last projection, in what the frame draws into.
void setViewport();
// The render target's pixels, rows packed, 0xAARRGGBB; empty if it can't be read.
std::span<const std::byte> readTarget(const Texture& target);
void updateTexture(const protocol::Command& command);
void createBuffer(const protocol::Command& command);
void updateBuffer(const protocol::Command& command);
void setProjection(const protocol::SetProjection& projection);
void setState(const protocol::SetState& state);
void drawTriangles(const protocol::DrawTriangles& draw);
void drawArrays(const protocol::Command& command);
void drawPointSprites(const protocol::Command& command);
// Draws indexCount indices from index, with vertex streams (positions required, the others may be null).
void draw(Diligent::IBuffer* indices, std::uint32_t indexCount, Diligent::IBuffer* positions,
Diligent::IBuffer* normals, Diligent::IBuffer* texCoords, Diligent::IBuffer* colors);
// The pipeline for the current state, with these vertex streams and textures, for triangles or particles.
Pipeline* pipeline(std::uint32_t streams, std::uint32_t textures, bool sprites);
const Shaders& shaders(std::uint32_t streams, std::uint32_t textures, bool sprites);
// Picks the pipeline for the current state and sets it up for a draw: constants, textures. Null if it can't.
Pipeline* prepare(std::uint32_t streams, bool sprites);
Diligent::ITextureView* textureView(std::uint32_t id);
Diligent::RefCntAutoPtr<Diligent::IRenderDevice> device_;
Diligent::RefCntAutoPtr<Diligent::IDeviceContext> context_;
Diligent::RefCntAutoPtr<Diligent::ISwapChain> swapChain_;
Diligent::RefCntAutoPtr<Diligent::IBuffer> constantBuffer_;
Diligent::RefCntAutoPtr<Diligent::ISampler> samplers_[4]; // by wrap u (1) and wrap v (2)
std::unordered_map<std::uint32_t, Texture> textures_;
std::unordered_map<std::uint32_t, Buffer> buffers_;
std::unordered_map<std::uint32_t, Shaders> shaders_;
std::unordered_map<std::uint32_t, Pipeline> pipelines_;
std::string shaderSource_;
Constants constants_{};
State state_;
// What the current frame draws into: the window, a render target, or nothing (a frame not drawn).
bool onscreen_ = false; // in a frame drawn into the window
Texture* target_ = nullptr; // in a frame drawn into this render target
bool readsBack_ = false; // the frame's render target is read back at its end
Diligent::ITextureView* colorView_ = nullptr;
Diligent::ITextureView* depthView_ = nullptr;
std::int32_t projectionWidth_ = 0;
std::int32_t projectionHeight_ = 0;
std::function<void(std::span<const std::byte>)> readBack_;
std::vector<std::byte> readPixels_;
bool draw_ = true;
std::uint64_t lastSentAt_ = 0;
int offsetX_ = 0;
};
} // namespace t5de