Fix particle sizes, and add tools for comparing renderers
Particles: Direct3D 9's size limit evidently isn't the documented 64 pixels; unclamped (up to 2048) they match the client's renderer. A compare view shows the left half's scene over the right half, both renderers side by side; %TEMP%\t5de-renderer-view.txt switches views and %TEMP%\t5de-renderer-shaders\fixed_function.hlsl replaces the shaders while the client runs. The renderer process runs from a copy, so the program can be replaced meanwhile. The adapter recognizes only the textures and buffers it made (others reach the client renderer unchanged) and logs the others and render targets. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
1a45bf9a86
commit
299cd8c312
11 files changed
+239
-28
No files matched your search
+95
-13
@@ -84,15 +84,90 @@ HWND create_child_window(HWND parent)
|
||||
0, 0, 1, 1, parent, nullptr, windowClass.hInstance, nullptr);
|
||||
}
|
||||
|
||||
// Where the child window goes in the client's 3D view.
|
||||
RECT placement(HWND parent, View view)
|
||||
// The view, as the adapter set it, or as %TEMP%\t5de-renderer-view.txt says while the client runs (off, split, full
|
||||
// or compare): for comparing the renderers without restarting the client. Read every second or so.
|
||||
View current_view(View adapter)
|
||||
{
|
||||
RECT client{};
|
||||
GetClientRect(parent, &client);
|
||||
if (view == View::Split) {
|
||||
client.left = client.right / 2;
|
||||
static View view = adapter;
|
||||
static ULONGLONG checked = 0;
|
||||
if (GetTickCount64() - checked < 1000) {
|
||||
return view;
|
||||
}
|
||||
return client;
|
||||
checked = GetTickCount64();
|
||||
view = adapter;
|
||||
wchar_t temp[MAX_PATH + 1];
|
||||
const DWORD length = GetTempPathW(MAX_PATH + 1, temp);
|
||||
if (length == 0 || length > MAX_PATH) {
|
||||
return view;
|
||||
}
|
||||
const HANDLE file = CreateFileW((std::filesystem::path(temp) / L"t5de-renderer-view.txt").c_str(), GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
return view;
|
||||
}
|
||||
char text[16] = {};
|
||||
DWORD read = 0;
|
||||
ReadFile(file, text, sizeof(text) - 1, &read, nullptr);
|
||||
CloseHandle(file);
|
||||
const std::string_view setting(text, std::strcspn(text, "\r\n "));
|
||||
view = setting == "off" ? View::Off
|
||||
: setting == "split" ? View::Split
|
||||
: setting == "full" ? View::Full
|
||||
: setting == "compare" ? View::Compare
|
||||
: adapter;
|
||||
return view;
|
||||
}
|
||||
|
||||
// Shader changes to try while the client runs: %TEMP%\t5de-renderer-shaders\fixed_function.hlsl, if there is
|
||||
// one, replaces the built-in shaders, and is read again when it changes (checked every second or so).
|
||||
void reload_shaders(t5de::Scene& scene)
|
||||
{
|
||||
static ULONGLONG checked = 0;
|
||||
static std::filesystem::file_time_type loaded{};
|
||||
if (GetTickCount64() - checked < 1000) {
|
||||
return;
|
||||
}
|
||||
checked = GetTickCount64();
|
||||
std::error_code error;
|
||||
const auto path = std::filesystem::temp_directory_path(error) / L"t5de-renderer-shaders" / L"fixed_function.hlsl";
|
||||
const auto modified = std::filesystem::last_write_time(path, error);
|
||||
if (error || modified == loaded) {
|
||||
return;
|
||||
}
|
||||
const HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
std::string source(static_cast<std::size_t>(std::filesystem::file_size(path, error)), ' ');
|
||||
DWORD read = 0;
|
||||
ReadFile(file, source.data(), static_cast<DWORD>(source.size()), &read, nullptr);
|
||||
CloseHandle(file);
|
||||
source.resize(read);
|
||||
loaded = modified;
|
||||
scene.setShaderSource(std::move(source));
|
||||
info("shaders reloaded from {}", path.string());
|
||||
}
|
||||
|
||||
// Where the child window goes in the client's 3D view, and where in the 3D view its scene starts (offset).
|
||||
struct Placement {
|
||||
RECT window{};
|
||||
int offset = 0;
|
||||
};
|
||||
|
||||
Placement placement(HWND parent, View view)
|
||||
{
|
||||
Placement place;
|
||||
GetClientRect(parent, &place.window);
|
||||
if (view == View::Split || view == View::Compare) {
|
||||
place.window.left = place.window.right / 2;
|
||||
}
|
||||
place.offset = view == View::Compare ? 0 : place.window.left;
|
||||
if (view == View::Off) {
|
||||
place.window = {};
|
||||
}
|
||||
return place;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -203,18 +278,25 @@ int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
||||
}
|
||||
|
||||
// Follow the client's 3D view's size.
|
||||
const RECT place = placement(clientWindow, channel->view);
|
||||
const auto [place, offset] = placement(clientWindow, current_view(channel->view));
|
||||
const int width = place.right - place.left;
|
||||
const int height = place.bottom - place.top;
|
||||
if (std::memcmp(&place, &placed, sizeof(RECT)) != 0 && width > 0 && height > 0) {
|
||||
SetWindowPos(window, nullptr, place.left, place.top, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
swapChain->Resize(static_cast<Uint32>(width), static_cast<Uint32>(height));
|
||||
if (std::memcmp(&place, &placed, sizeof(RECT)) != 0) {
|
||||
placed = place;
|
||||
info("drawing {}x{} at {} in the client's 3D view", width, height, place.left);
|
||||
if (width > 0 && height > 0) {
|
||||
SetWindowPos(window, nullptr, place.left, place.top, width, height,
|
||||
SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
swapChain->Resize(static_cast<Uint32>(width), static_cast<Uint32>(height));
|
||||
info("drawing {}x{} at {} in the client's 3D view, its scene from {}", width, height, place.left, offset);
|
||||
} else {
|
||||
ShowWindow(window, SW_HIDE);
|
||||
info("hidden");
|
||||
}
|
||||
}
|
||||
reload_shaders(scene);
|
||||
LARGE_INTEGER start, end;
|
||||
QueryPerformanceCounter(&start);
|
||||
if (scene.execute(std::span(frame).first(complete), place.left, width > 0 && height > 0)) {
|
||||
if (scene.execute(std::span(frame).first(complete), offset, width > 0 && height > 0)) {
|
||||
swapChain->Present(0);
|
||||
QueryPerformanceCounter(&end);
|
||||
drawTime += static_cast<double>(end.QuadPart - start.QuadPart) / static_cast<double>(frequency.QuadPart);
|
||||
|
||||
+32
-2
@@ -1,5 +1,8 @@
|
||||
#include "scene.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
@@ -56,7 +59,7 @@ constexpr std::uint32_t hasColors = 4;
|
||||
} // namespace
|
||||
|
||||
Scene::Scene(IRenderDevice* device, IDeviceContext* context, ISwapChain* swapChain)
|
||||
: device_(device), context_(context), swapChain_(swapChain)
|
||||
: device_(device), context_(context), swapChain_(swapChain), shaderSource_(fixedFunctionHlsl)
|
||||
{
|
||||
BufferDesc constants;
|
||||
constants.Name = "Constants";
|
||||
@@ -86,6 +89,13 @@ Scene::Scene(IRenderDevice* device, IDeviceContext* context, ISwapChain* swapCha
|
||||
constants_.flags[2] = 127; // the client's alpha reference
|
||||
}
|
||||
|
||||
void Scene::setShaderSource(std::string source)
|
||||
{
|
||||
shaderSource_ = std::move(source);
|
||||
shaders_.clear();
|
||||
pipelines_.clear();
|
||||
}
|
||||
|
||||
bool Scene::execute(std::span<const std::byte> commands, int offsetX, bool draw)
|
||||
{
|
||||
offsetX_ = offsetX;
|
||||
@@ -546,6 +556,26 @@ void Scene::drawPointSprites(const protocol::Command& command)
|
||||
constants_.ambient[1], constants_.ambient[2], constants_.texTransform0[0], constants_.texTransform0[1],
|
||||
constants_.texTransform0[2], constants_.texTransform0[4], constants_.texTransform0[5],
|
||||
constants_.texTransform0[6]);
|
||||
// The first sprite: where it is, seen from the camera, and how big it's drawn.
|
||||
float point[4] = {0, 0, 0, 1};
|
||||
std::uint32_t color = 0;
|
||||
std::memcpy(point, payload.data(), 12);
|
||||
std::memcpy(&color, payload.data() + 12, 4);
|
||||
const auto transform = [](const float (&p)[4], const float (&m)[16]) {
|
||||
std::array<float, 4> out{};
|
||||
for (int column = 0; column < 4; ++column) {
|
||||
out[column] = p[0] * m[column] + p[1] * m[4 + column] + p[2] * m[8 + column] + p[3] * m[12 + column];
|
||||
}
|
||||
return out;
|
||||
};
|
||||
const auto world = transform(point, constants_.world);
|
||||
const float worldPoint[4] = {world[0], world[1], world[2], world[3]};
|
||||
const auto view = transform(worldPoint, constants_.view);
|
||||
const float distance = std::sqrt(view[0] * view[0] + view[1] * view[1] + view[2] * view[2]);
|
||||
log::info(" first sprite at ({:.2f} {:.2f} {:.2f}), color {:08x}, from the camera ({:.2f} {:.2f} {:.2f}): "
|
||||
"distance {:.2f}, {:.1f} pixels across (viewport height {})",
|
||||
point[0], point[1], point[2], color, view[0], view[1], view[2], distance,
|
||||
std::clamp(constants_.viewport[1] / std::max(distance, 1e-4f), 1.0f, 2048.0f), constants_.viewport[1]);
|
||||
}
|
||||
IBuffer* buffers[] = {buffer};
|
||||
context_->SetVertexBuffers(0, 1, buffers, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
|
||||
@@ -565,7 +595,7 @@ const Scene::Shaders& Scene::shaders(std::uint32_t streams, std::uint32_t textur
|
||||
const std::string source = std::string("#define HAS_NORMALS ") + ((streams & hasNormals) != 0 ? "1" : "0") +
|
||||
"\n#define HAS_TEX_COORDS " + ((streams & hasTexCoords) != 0 ? "1" : "0") +
|
||||
"\n#define HAS_COLORS " + ((streams & hasColors) != 0 ? "1" : "0") +
|
||||
"\n#define TEXTURES " + std::to_string(textures) + "\n" + fixedFunctionHlsl;
|
||||
"\n#define TEXTURES " + std::to_string(textures) + "\n" + shaderSource_;
|
||||
Shaders created;
|
||||
ShaderCreateInfo info;
|
||||
info.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "EngineFactoryD3D11.h"
|
||||
@@ -23,6 +24,9 @@ public:
|
||||
// present).
|
||||
bool execute(std::span<const std::byte> commands, int offsetX, bool draw);
|
||||
|
||||
// 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_; }
|
||||
|
||||
@@ -111,6 +115,7 @@ private:
|
||||
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_;
|
||||
|
||||
@@ -101,7 +101,7 @@ void vs_sprite(in SpriteInput input, out PSInput output)
|
||||
{
|
||||
float4 view = mul(mul(float4(input.Position, 1.0), g_World), g_View);
|
||||
float4 clip = mul(view, g_Projection);
|
||||
float size = clamp(g_Viewport.y / max(length(view.xyz), 1e-4), 1.0, 64.0);
|
||||
float size = clamp(g_Viewport.y / max(length(view.xyz), 1e-4), 1.0, 2048.0);
|
||||
float2 corner = float2((input.Corner & 1) != 0 ? 1.0 : -1.0, (input.Corner & 2) != 0 ? -1.0 : 1.0);
|
||||
clip.xy += corner * size / g_Viewport.xy * clip.w; // half the size each way, in clip space
|
||||
output.Position = clip;
|
||||
|
||||
Reference in new issue
Block a user