载入中...
搜索中...
未找到
PixelWorldGraphics.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4#include "graphics/Graphics.h"
7
8#include <simplesquirrel/simplesquirrel.hpp>
9
10#include <algorithm>
11#include <array>
12#include <cstddef>
13#include <functional>
14#include <utility>
15#include <vector>
16
18
19namespace {
20
21using Rgba = std::array<std::uint8_t, 4>;
22
24 const std::uint32_t rgba = world.materialDisplayRgba(material);
25 return {std::uint8_t(rgba >> 24U), std::uint8_t(rgba >> 16U),
26 std::uint8_t(rgba >> 8U), std::uint8_t(rgba)};
27}
28
29} // namespace
30
32 int originX = 0;
33 int originY = 0;
34 int width = 0;
35 int height = 0;
36 std::vector<std::uint8_t> rgba;
38 std::uint64_t renderedRevision = 0;
39 std::uint64_t totalUploadedChunks = 0;
40 std::uint64_t uploadCount = 0;
41 bool initialized = false;
42};
43
45 : impl_(std::make_unique<Impl>()) {
46 if (width <= 0 || height <= 0 || width > 8192 || height > 8192)
47 throw eve::Exception("PixelWorld atlas size must be within 1..8192");
48 impl_->originX = originX;
49 impl_->originY = originY;
50 impl_->width = width;
51 impl_->height = height;
52 impl_->rgba.resize(std::size_t(width) * std::size_t(height) * 4U, 0);
53}
54
58
59int PixelWorldAtlasRenderer::sync(pixelworld::PixelWorld* world, graphics::Graphics* graphics) {
60 if (!world || !graphics) throw eve::Exception("PixelWorldAtlasRenderer::sync requires world and graphics");
61
62 const std::uint64_t worldRevision = world->revision();
63 const bool rolledBack = impl_->initialized && worldRevision < impl_->renderedRevision;
64 if (rolledBack) {
65 std::fill(impl_->rgba.begin(), impl_->rgba.end(), std::uint8_t(0));
66 impl_->renderedRevision = 0;
67 }
68
69 auto chunks = world->snapshotChangedChunks(impl_->initialized && !rolledBack
70 ? impl_->renderedRevision
71 : 0);
72 if (impl_->initialized && !rolledBack && chunks.empty()) {
73 impl_->renderedRevision = worldRevision;
74 return 0;
75 }
76
77 struct DirtyRect {
78 int x;
79 int y;
80 int width;
81 int height;
82 };
83 std::vector<DirtyRect> dirtyRects;
84 dirtyRects.reserve(chunks.size());
85 for (const auto& chunk : chunks) {
86 const int chunkOriginX = chunk.x * pixelworld::kPixelChunkSize;
87 const int chunkOriginY = chunk.y * pixelworld::kPixelChunkSize;
88 const int minX = std::max(chunkOriginX, impl_->originX);
89 const int minY = std::max(chunkOriginY, impl_->originY);
90 const int maxX = std::min(chunkOriginX + pixelworld::kPixelChunkSize,
91 impl_->originX + impl_->width);
92 const int maxY = std::min(chunkOriginY + pixelworld::kPixelChunkSize,
93 impl_->originY + impl_->height);
94 if (minX >= maxX || minY >= maxY) continue;
95 for (int worldY = minY; worldY < maxY; ++worldY) {
96 for (int worldX = minX; worldX < maxX; ++worldX) {
97 const int chunkX = worldX - chunkOriginX;
98 const int chunkY = worldY - chunkOriginY;
99 const Rgba pixel = chunk.removed
100 ? Rgba{0, 0, 0, 0}
101 : color(*world, chunk.cells[std::size_t(chunkY) *
103 std::size_t(chunkX)]
104 .material);
105 const std::size_t atlasOffset =
106 (std::size_t(worldY - impl_->originY) * std::size_t(impl_->width) +
107 std::size_t(worldX - impl_->originX)) * 4U;
108 std::copy(pixel.begin(), pixel.end(), impl_->rgba.begin() + std::ptrdiff_t(atlasOffset));
109 }
110 }
111 dirtyRects.push_back({minX - impl_->originX, minY - impl_->originY,
112 maxX - minX, maxY - minY});
113 }
114
115 const bool creatingTexture = !impl_->texture;
116 if (creatingTexture) {
117 impl_->texture = graphics->newTexture(impl_->width, impl_->height, impl_->rgba.data(), false, false);
118 graphics->setTextureSampler(impl_->texture, "nearest", "none", 1.0F, 0.0F);
119 } else if (rolledBack) {
120 graphics->updateTextureRegion(impl_->texture, 0, 0, impl_->width, impl_->height,
121 impl_->rgba).expect("PixelWorld atlas reset upload failed");
122 } else if (!dirtyRects.empty()) {
123 const std::size_t atlasStride = std::size_t(impl_->width) * 4U;
124 std::vector<graphics::TextureRegionUpload> uploads;
125 uploads.reserve(dirtyRects.size());
126 for (const DirtyRect rect : dirtyRects) {
127 const std::size_t offset =
128 std::size_t(rect.y) * atlasStride + std::size_t(rect.x) * 4U;
129 uploads.push_back({rect.x, rect.y, rect.width, rect.height,
130 std::span<const std::uint8_t>(impl_->rgba).subspan(offset),
131 atlasStride});
132 }
133 graphics->updateTextureRegions(impl_->texture, uploads)
134 .expect("PixelWorld dirty chunk upload failed");
135 }
136 impl_->initialized = true;
137 impl_->renderedRevision = worldRevision;
138 impl_->totalUploadedChunks += dirtyRects.size();
139 if (creatingTexture || rolledBack || !dirtyRects.empty()) ++impl_->uploadCount;
140 return int(dirtyRects.size());
141}
142
144 graphics::Graphics* graphics, float scale) {
145 if (!world || !graphics || scale <= 0.0F)
146 throw eve::Exception("PixelWorldAtlasRenderer::drawDiagnostics requires world, graphics and positive scale");
147 const int minChunkX = impl_->originX >= 0
148 ? impl_->originX / pixelworld::kPixelChunkSize
149 : -((-impl_->originX + pixelworld::kPixelChunkSize - 1) /
151 const int minChunkY = impl_->originY >= 0
152 ? impl_->originY / pixelworld::kPixelChunkSize
153 : -((-impl_->originY + pixelworld::kPixelChunkSize - 1) /
155 const int lastX = impl_->originX + impl_->width - 1;
156 const int lastY = impl_->originY + impl_->height - 1;
157 const int maxChunkX = lastX >= 0 ? lastX / pixelworld::kPixelChunkSize
158 : -((-lastX + pixelworld::kPixelChunkSize - 1) /
160 const int maxChunkY = lastY >= 0 ? lastY / pixelworld::kPixelChunkSize
161 : -((-lastY + pixelworld::kPixelChunkSize - 1) /
163 const auto diagnostics = world->chunkDiagnostics({minChunkX, minChunkY, maxChunkX, maxChunkY})
164 .expect("PixelWorld diagnostic overlay region is bounded");
165 for (const auto& chunk : diagnostics) {
166 const float x = float(chunk.x * pixelworld::kPixelChunkSize - impl_->originX) * scale;
167 const float y = float(chunk.y * pixelworld::kPixelChunkSize - impl_->originY) * scale;
168 const float size = float(pixelworld::kPixelChunkSize) * scale;
169 const float heat = std::clamp((float(chunk.maximumTemperature) - 20.0F) / 1000.0F,
170 0.0F, 1.0F);
171 const float red = heat;
172 const float green = chunk.active ? 0.95F : 0.25F;
173 const float blue = chunk.active ? 0.25F : 0.95F;
174 graphics->drawSolidRectRGBA(x, y, size, 1.0F, red, green, blue, 0.9F);
175 graphics->drawSolidRectRGBA(x, y + size - 1.0F, size, 1.0F, red, green, blue, 0.9F);
176 graphics->drawSolidRectRGBA(x, y, 1.0F, size, red, green, blue, 0.9F);
177 graphics->drawSolidRectRGBA(x + size - 1.0F, y, 1.0F, size, red, green, blue, 0.9F);
178 }
179
180 const auto samples = pixelworld::pixelWorldControlService()
181 .performanceSamples(world->worldLink().world, 60)
182 .expect("live PixelWorld has performance samples");
183 constexpr float graphWidth = 124.0F;
184 constexpr float graphHeight = 42.0F;
185 const float graphX = float(impl_->width) * scale - graphWidth - 8.0F;
186 const float graphY = 8.0F;
187 graphics->drawSolidRectRGBA(graphX, graphY, graphWidth, graphHeight,
188 0.015F, 0.02F, 0.035F, 0.82F);
189 std::uint64_t maximum = 1;
190 for (const auto& sample : samples) maximum = std::max(maximum, sample.elapsedMicroseconds);
191 const float barWidth = 2.0F;
192 const std::size_t visible = std::min<std::size_t>(samples.size(), 60);
193 for (std::size_t index = 0; index < visible; ++index) {
194 const auto& sample = samples[samples.size() - visible + index];
195 const float height = std::max(1.0F, float(sample.elapsedMicroseconds) / float(maximum) *
196 (graphHeight - 6.0F));
197 graphics->drawSolidRectRGBA(graphX + 2.0F + float(index) * barWidth,
198 graphY + graphHeight - 2.0F - height,
199 1.0F, height, 0.25F, 0.9F, 1.0F, 0.95F);
200 }
201 return int(diagnostics.size());
202}
203
204graphics::Texture* PixelWorldAtlasRenderer::texture() const noexcept { return impl_->texture; }
205int PixelWorldAtlasRenderer::originX() const noexcept { return impl_->originX; }
206int PixelWorldAtlasRenderer::originY() const noexcept { return impl_->originY; }
207int PixelWorldAtlasRenderer::width() const noexcept { return impl_->width; }
208int PixelWorldAtlasRenderer::height() const noexcept { return impl_->height; }
209std::uint64_t PixelWorldAtlasRenderer::renderedRevision() const noexcept { return impl_->renderedRevision; }
210std::uint64_t PixelWorldAtlasRenderer::totalUploadedChunks() const noexcept { return impl_->totalUploadedChunks; }
211std::uint64_t PixelWorldAtlasRenderer::uploadCount() const noexcept { return impl_->uploadCount; }
212
214
216 return new PixelWorldAtlasRenderer(originX, originY, width, height);
217}
218
219void PixelWorldGraphics::expose(ssq::Table& table) {
220 auto module = table.addClass(name, PixelWorldGraphics::create, false);
221 module.addFunc("newRenderer", &PixelWorldGraphics::newRenderer);
222 auto renderer = table.addClass<PixelWorldAtlasRenderer>(
223 "PixelWorldAtlasRenderer",
224 std::function<PixelWorldAtlasRenderer*()>([]() -> PixelWorldAtlasRenderer* { return nullptr; }),
225 true);
226 renderer.addFunc("sync", &PixelWorldAtlasRenderer::sync);
227 renderer.addFunc("drawDiagnostics", &PixelWorldAtlasRenderer::drawDiagnostics);
228 renderer.addFunc("getTexture", [](PixelWorldAtlasRenderer* self) { return self->texture(); });
229 renderer.addFunc("getOriginX", [](PixelWorldAtlasRenderer* self) { return self->originX(); });
230 renderer.addFunc("getOriginY", [](PixelWorldAtlasRenderer* self) { return self->originY(); });
231 renderer.addFunc("getWidth", [](PixelWorldAtlasRenderer* self) { return self->width(); });
232 renderer.addFunc("getHeight", [](PixelWorldAtlasRenderer* self) { return self->height(); });
233 renderer.addFunc("getRenderedRevision", [](PixelWorldAtlasRenderer* self) {
234 return std::int64_t(self->renderedRevision());
235 });
236 renderer.addFunc("getTotalUploadedChunks", [](PixelWorldAtlasRenderer* self) {
237 return std::int64_t(self->totalUploadedChunks());
238 });
239 renderer.addFunc("getUploadCount", [](PixelWorldAtlasRenderer* self) {
240 return std::int64_t(self->uploadCount());
241 });
242}
243
244void PixelWorldGraphics::expose(ssq::Class& cls) {
245 cls.addFunc("newRenderer", &PixelWorldGraphics::newRenderer);
246}
247
248} // namespace eve::pixelworld_graphics
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
HSQOBJECT cls
Definition ECS.cpp:21
float maximum[3]
std::uint32_t height
std::uint32_t width
size_t offset
std::array< float, 3 > scale
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::map< std::shared_ptr< const std::vector< uint8_t > >, std::shared_ptr< DeviceBytes >, std::owner_less< std::shared_ptr< const std::vector< uint8_t > > > > uploads
Definition OnnxGpgpu.cpp:50
World3D * world
Material * material
bool visible
float size
Definition TreeMesh.cpp:156
uint32_t index
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
virtual void drawSolidRectRGBA(float x, float y, float w, float h, float r, float g, float b, float a=1.f)
Draws solid rect rgba.
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
eve::Result< std::vector< PixelWorldPerformanceSample > > performanceSamples(std::uint64_t worldId, std::uint32_t limit=120) const
Copy up to the latest 256 simulation performance samples.
Sparse, chunked, deterministic 2D falling-material world.
Definition PixelWorld.h:224
Render-thread-affine RGBA atlas projection of a rectangular PixelWorld region. @ownership The rendere...
int drawDiagnostics(pixelworld::PixelWorld *world, graphics::Graphics *graphics, float scale)
Draw Chunk activity/temperature outlines and a bounded step-time graph.
std::uint64_t renderedRevision() const noexcept
Rendered revision.
std::uint64_t totalUploadedChunks() const noexcept
Total uploaded chunks.
PixelWorldAtlasRenderer(int originX, int originY, int width, int height)
Create a transparent atlas for [origin, origin + size). Throws on invalid size.
graphics::Texture * texture() const noexcept
Borrow the stable backend-owned atlas texture, or null before first sync. @ownership Graphics owns th...
int sync(pixelworld::PixelWorld *world, graphics::Graphics *graphics)
Patch changed CPU chunks and upload the atlas when its world revision advances.
std::uint64_t uploadCount() const noexcept
Uploads count.
~PixelWorldAtlasRenderer()
Pixel world atlas renderer.
Optional rendering satellite for the backend-neutral PixelWorld module.
PixelWorldAtlasRenderer * newRenderer(int originX, int originY, int width, int height)
Create a caller-owned atlas renderer for one world-space rectangle.
PixelWorldControlService & pixelWorldControlService()
Process-lifetime PixelWorld tooling registry.
MaterialId
Compact stable material identifier stored in each authoritative cell.
constexpr int kPixelChunkSize
Definition PixelWorld.h:20
glm::vec4 color