test: add particle gpu ping-pong validation
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
54
include/mbgl/shaders/gl/particle_gpu_validation.hpp
Normal file
54
include/mbgl/shaders/gl/particle_gpu_validation.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Generated code, do not modify this file!
|
||||
#pragma once
|
||||
#include <mbgl/shaders/shader_source.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
template <>
|
||||
struct ShaderSource<BuiltIn::ParticleGpuValidationShader, gfx::Backend::Type::OpenGL> {
|
||||
static constexpr const char* name = "ParticleGpuValidationShader";
|
||||
static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos;
|
||||
|
||||
out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);
|
||||
v_uv = a_pos;
|
||||
}
|
||||
)";
|
||||
static constexpr const char* fragment = R"(layout (std140) uniform ParticleGpuValidationUBO {
|
||||
highp vec4 u_pass;
|
||||
highp vec4 u_marker;
|
||||
};
|
||||
|
||||
uniform sampler2D u_previous;
|
||||
in vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
vec4 previous = texture(u_previous, v_uv);
|
||||
float mode = u_pass.x;
|
||||
|
||||
if (mode < 0.5) {
|
||||
// State pass: deterministic GPU-only state evolution.
|
||||
fragColor = vec4(fract(previous.r + 0.03125), v_uv, 1.0);
|
||||
} else if (mode < 1.5) {
|
||||
// Trail pass: fade the previous trail and add a moving validation marker.
|
||||
float marker = 1.0 - smoothstep(0.0, u_marker.z, distance(v_uv, u_marker.xy));
|
||||
vec3 color = previous.rgb * u_marker.w + marker * vec3(0.08, 0.55, 1.0);
|
||||
fragColor = vec4(min(color, vec3(1.0)), 1.0);
|
||||
} else {
|
||||
// Main pass: composite the latest trail without hiding the validation map.
|
||||
float intensity = max(previous.r, max(previous.g, previous.b));
|
||||
fragColor = vec4(previous.rgb * 0.72, min(intensity * 0.72, 0.72));
|
||||
}
|
||||
|
||||
#ifdef OVERDRAW_INSPECTOR
|
||||
fragColor = vec4(1.0);
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
@@ -149,6 +149,13 @@ struct ShaderInfo<BuiltIn::HeatmapTextureShader, gfx::Backend::Type::OpenGL> {
|
||||
static const std::vector<TextureInfo> textures;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ShaderInfo<BuiltIn::ParticleGpuValidationShader, gfx::Backend::Type::OpenGL> {
|
||||
static const std::vector<AttributeInfo> attributes;
|
||||
static const std::vector<UniformBlockInfo> uniformBlocks;
|
||||
static const std::vector<TextureInfo> textures;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ShaderInfo<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::OpenGL> {
|
||||
static const std::vector<AttributeInfo> attributes;
|
||||
|
||||
16
include/mbgl/shaders/particle_gpu_validation_ubo.hpp
Normal file
16
include/mbgl/shaders/particle_gpu_validation_ubo.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
// VALIDATION ONLY: parameters for the 30-02 State/Trail ping-pong proof.
|
||||
struct alignas(16) ParticleGpuValidationUBO {
|
||||
std::array<float, 4> pass;
|
||||
std::array<float, 4> marker;
|
||||
};
|
||||
static_assert(sizeof(ParticleGpuValidationUBO) == 2 * 16);
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
@@ -127,6 +127,11 @@ enum {
|
||||
heatmapTextureUBOCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idParticleGpuValidationUBO = getEnumValue(drawableReservedUBOCount, layerUBOStartId),
|
||||
particleGpuValidationUBOCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idHillshadeEvaluatedPropsUBO = getEnumValue(hillshadeLayerSSBOCount, layerUBOStartId),
|
||||
hillshadeLayerUBOCount
|
||||
@@ -268,6 +273,7 @@ static constexpr uint32_t maxUBOCountPerShader = std::max({static_cast<uint32_t>
|
||||
static_cast<uint32_t>(fillUBOCount),
|
||||
static_cast<uint32_t>(fillExtrusionUBOCount),
|
||||
static_cast<uint32_t>(heatmapTextureUBOCount),
|
||||
static_cast<uint32_t>(particleGpuValidationUBOCount),
|
||||
static_cast<uint32_t>(heatmapUBOCount),
|
||||
static_cast<uint32_t>(hillshadePrepareUBOCount),
|
||||
static_cast<uint32_t>(hillshadeUBOCount),
|
||||
@@ -332,6 +338,11 @@ enum {
|
||||
heatmapTextureCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idParticleGpuValidationPreviousTexture,
|
||||
particleGpuValidationTextureCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idHillshadeImageTexture,
|
||||
hillshadeTextureCount
|
||||
@@ -376,6 +387,7 @@ static constexpr uint32_t maxTextureCountPerShader = std::max({static_cast<uint3
|
||||
static_cast<uint32_t>(fillTextureCount),
|
||||
static_cast<uint32_t>(fillExtrusionTextureCount),
|
||||
static_cast<uint32_t>(heatmapTextureCount),
|
||||
static_cast<uint32_t>(particleGpuValidationTextureCount),
|
||||
static_cast<uint32_t>(hillshadeTextureCount),
|
||||
static_cast<uint32_t>(colorReliefTextureCount),
|
||||
static_cast<uint32_t>(lineTextureCount),
|
||||
@@ -389,6 +401,11 @@ enum {
|
||||
backgroundVertexAttributeCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idParticleGpuValidationPosVertexAttribute,
|
||||
particleGpuValidationVertexAttributeCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idCirclePosVertexAttribute,
|
||||
|
||||
@@ -556,6 +573,7 @@ enum {
|
||||
|
||||
static constexpr uint32_t maxVertexAttributeCountPerShader = std::max({
|
||||
static_cast<uint32_t>(backgroundVertexAttributeCount),
|
||||
static_cast<uint32_t>(particleGpuValidationVertexAttributeCount),
|
||||
static_cast<uint32_t>(circleVertexAttributeCount),
|
||||
static_cast<uint32_t>(clippingMaskVertexAttributeCount),
|
||||
static_cast<uint32_t>(collisionVertexAttributeCount),
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <mbgl/shaders/gl/fill_extrusion_pattern_instanced.hpp>
|
||||
#include <mbgl/shaders/gl/heatmap.hpp>
|
||||
#include <mbgl/shaders/gl/heatmap_texture.hpp>
|
||||
#include <mbgl/shaders/gl/particle_gpu_validation.hpp>
|
||||
#include <mbgl/shaders/gl/hillshade_prepare.hpp>
|
||||
#include <mbgl/shaders/gl/color_relief.hpp>
|
||||
#include <mbgl/shaders/gl/hillshade.hpp>
|
||||
|
||||
@@ -30,6 +30,7 @@ enum class BuiltIn {
|
||||
FillExtrusionPatternInstancedShader,
|
||||
HeatmapShader,
|
||||
HeatmapTextureShader,
|
||||
ParticleGpuValidationShader,
|
||||
HillshadePrepareShader,
|
||||
ColorReliefShader,
|
||||
HillshadeShader,
|
||||
|
||||
@@ -12,11 +12,13 @@ import org.maplibre.android.maps.MapLibreMap
|
||||
import org.maplibre.android.maps.Style
|
||||
import org.maplibre.android.testapp.R
|
||||
|
||||
/** VALIDATION ONLY: Android OpenGL host for the 30-01 Core RenderLayer experiment. */
|
||||
/** VALIDATION ONLY: Android OpenGL host for the 30-01/30-02 Core RenderLayer experiments. */
|
||||
class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
||||
private lateinit var mapView: MapView
|
||||
private lateinit var maplibreMap: MapLibreMap
|
||||
private lateinit var status: TextView
|
||||
private var active = true
|
||||
private var enabled = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -31,29 +33,47 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
||||
.target(LatLng(0.0, 0.0))
|
||||
.zoom(2.0)
|
||||
.build()
|
||||
loadValidationStyle(active = true)
|
||||
loadValidationStyle(active = true, enabled = true)
|
||||
}
|
||||
|
||||
findViewById<Button>(R.id.validationActive).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = true)
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = true, enabled = enabled)
|
||||
}
|
||||
findViewById<Button>(R.id.validationStatic).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = false)
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = false, enabled = enabled)
|
||||
}
|
||||
findViewById<Button>(R.id.validationDisable).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = active, enabled = false)
|
||||
}
|
||||
findViewById<Button>(R.id.validationEnable).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = active, enabled = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadValidationStyle(active: Boolean) {
|
||||
val state = if (active) "ACTIVE" else "STATIC"
|
||||
Log.i(TAG, "ParticleValidation: Test App switching to $state")
|
||||
maplibreMap.setStyle(Style.Builder().fromJson(validationStyle(active))) {
|
||||
private fun loadValidationStyle(active: Boolean, enabled: Boolean) {
|
||||
this.active = active
|
||||
this.enabled = enabled
|
||||
val state = if (!enabled) "DISABLED" else if (active) "ACTIVE" else "STATIC"
|
||||
Log.i(TAG, "ParticleValidation30_02: Test App switching to $state")
|
||||
maplibreMap.setStyle(Style.Builder().fromJson(validationStyle(active, enabled))) {
|
||||
status.text = getString(
|
||||
if (active) R.string.particle_validation_active_status
|
||||
else R.string.particle_validation_static_status
|
||||
when {
|
||||
!enabled -> R.string.particle_validation_disabled_status
|
||||
active -> R.string.particle_validation_active_status
|
||||
else -> R.string.particle_validation_static_status
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validationStyle(active: Boolean) = """
|
||||
private fun validationStyle(active: Boolean, enabled: Boolean): String {
|
||||
val particleLayer = if (enabled) {
|
||||
"""{"id":"particle-validation-core","type":"particle-validation",
|
||||
"paint":{"particle-validation-active":$active}},"""
|
||||
} else {
|
||||
""
|
||||
}
|
||||
return """
|
||||
{
|
||||
"version": 8,
|
||||
"name": "Particle Core Layer Validation",
|
||||
@@ -67,13 +87,13 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
||||
{"id":"validation-background","type":"background","paint":{"background-color":"#102030"}},
|
||||
{"id":"validation-below","type":"circle","source":"validation-point",
|
||||
"paint":{"circle-radius":46,"circle-color":"#247ba0"}},
|
||||
{"id":"particle-validation-core","type":"particle-validation",
|
||||
"paint":{"particle-validation-active":$active}},
|
||||
$particleLayer
|
||||
{"id":"validation-above","type":"circle","source":"validation-point",
|
||||
"paint":{"circle-radius":16,"circle-color":"#ffe066"}}
|
||||
]
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
override fun onStart() { super.onStart(); mapView.onStart() }
|
||||
override fun onResume() { super.onResume(); mapView.onResume() }
|
||||
|
||||
@@ -43,6 +43,26 @@
|
||||
android:layout_weight="1"
|
||||
android:text="@string/particle_validation_static" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/validationDisable"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/particle_validation_disable" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/validationEnable"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/particle_validation_enable" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
<string name="category_data">Data Sources</string>
|
||||
<string name="particle_validation_active">ACTIVE</string>
|
||||
<string name="particle_validation_static">STATIC</string>
|
||||
<string name="particle_validation_active_status">ACTIVE: Core Layer requests MapLibre frames. Move, zoom, rotate and pitch the map.</string>
|
||||
<string name="particle_validation_static_status">STATIC: Core Layer stopped requesting frames. Map gestures must still render normally.</string>
|
||||
<string name="particle_validation_disable">DISABLE</string>
|
||||
<string name="particle_validation_enable">ENABLE</string>
|
||||
<string name="particle_validation_active_status">30-02 ACTIVE: GPU State/Trail ping-pong requests frames. Move, zoom, rotate, pitch and resize.</string>
|
||||
<string name="particle_validation_static_status">30-02 STATIC: GPU ping-pong stopped requesting frames. Gestures must still render normally.</string>
|
||||
<string name="particle_validation_disabled_status">30-02 DISABLED: validation layer and its GPU resources were removed. Tap ENABLE to recreate them.</string>
|
||||
</resources>
|
||||
|
||||
@@ -144,6 +144,13 @@
|
||||
"glsl_frag": "heatmap_texture.fragment.glsl",
|
||||
"uses_ubos": true
|
||||
},
|
||||
{
|
||||
"name": "ParticleGpuValidationShader",
|
||||
"header": "particle_gpu_validation",
|
||||
"glsl_vert": "particle_gpu_validation.vertex.glsl",
|
||||
"glsl_frag": "particle_gpu_validation.fragment.glsl",
|
||||
"uses_ubos": true
|
||||
},
|
||||
{
|
||||
"name": "HillshadePrepareShader",
|
||||
"header": "hillshade_prepare",
|
||||
|
||||
30
shaders/particle_gpu_validation.fragment.glsl
Normal file
30
shaders/particle_gpu_validation.fragment.glsl
Normal file
@@ -0,0 +1,30 @@
|
||||
layout (std140) uniform ParticleGpuValidationUBO {
|
||||
highp vec4 u_pass;
|
||||
highp vec4 u_marker;
|
||||
};
|
||||
|
||||
uniform sampler2D u_previous;
|
||||
in vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
vec4 previous = texture(u_previous, v_uv);
|
||||
float mode = u_pass.x;
|
||||
|
||||
if (mode < 0.5) {
|
||||
// State pass: deterministic GPU-only state evolution.
|
||||
fragColor = vec4(fract(previous.r + 0.03125), v_uv, 1.0);
|
||||
} else if (mode < 1.5) {
|
||||
// Trail pass: fade the previous trail and add a moving validation marker.
|
||||
float marker = 1.0 - smoothstep(0.0, u_marker.z, distance(v_uv, u_marker.xy));
|
||||
vec3 color = previous.rgb * u_marker.w + marker * vec3(0.08, 0.55, 1.0);
|
||||
fragColor = vec4(min(color, vec3(1.0)), 1.0);
|
||||
} else {
|
||||
// Main pass: composite the latest trail without hiding the validation map.
|
||||
float intensity = max(previous.r, max(previous.g, previous.b));
|
||||
fragColor = vec4(previous.rgb * 0.72, min(intensity * 0.72, 0.72));
|
||||
}
|
||||
|
||||
#ifdef OVERDRAW_INSPECTOR
|
||||
fragColor = vec4(1.0);
|
||||
#endif
|
||||
}
|
||||
8
shaders/particle_gpu_validation.vertex.glsl
Normal file
8
shaders/particle_gpu_validation.vertex.glsl
Normal file
@@ -0,0 +1,8 @@
|
||||
layout (location = 0) in vec2 a_pos;
|
||||
|
||||
out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);
|
||||
v_uv = a_pos;
|
||||
}
|
||||
@@ -135,6 +135,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar
|
||||
shaders::BuiltIn::FillExtrusionPatternShader,
|
||||
shaders::BuiltIn::HeatmapShader,
|
||||
shaders::BuiltIn::HeatmapTextureShader,
|
||||
shaders::BuiltIn::ParticleGpuValidationShader,
|
||||
shaders::BuiltIn::HillshadePrepareShader,
|
||||
shaders::BuiltIn::HillshadeShader,
|
||||
shaders::BuiltIn::ColorReliefShader,
|
||||
|
||||
@@ -1,78 +1,285 @@
|
||||
#include <mbgl/renderer/layers/render_particle_validation_layer.hpp>
|
||||
|
||||
#include <mbgl/gfx/color_mode.hpp>
|
||||
#include <mbgl/gfx/context.hpp>
|
||||
#include <mbgl/gfx/cull_face_mode.hpp>
|
||||
#include <mbgl/gfx/drawable.hpp>
|
||||
#include <mbgl/gfx/drawable_builder.hpp>
|
||||
#include <mbgl/gfx/drawable_tweaker.hpp>
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/renderer/buckets/heatmap_bucket.hpp>
|
||||
#include <mbgl/renderer/change_request.hpp>
|
||||
#include <mbgl/renderer/layer_group.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#include <mbgl/renderer/render_pass.hpp>
|
||||
#include <mbgl/renderer/render_static_data.hpp>
|
||||
#include <mbgl/renderer/render_target.hpp>
|
||||
#include <mbgl/shaders/particle_gpu_validation_ubo.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/shader_program_base.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto ShaderName = "ParticleGpuValidationShader";
|
||||
constexpr Size StateSize{16, 16};
|
||||
|
||||
std::atomic_uint64_t nextGpuGeneration{0};
|
||||
|
||||
const style::ParticleValidationLayer::Impl& impl(const Immutable<style::Layer::Impl>& value) {
|
||||
assert(value->getTypeInfo() == style::ParticleValidationLayer::Impl::staticTypeInfo());
|
||||
return static_cast<const style::ParticleValidationLayer::Impl&>(*value);
|
||||
}
|
||||
|
||||
class ParticleValidationDrawableTweaker final : public gfx::DrawableTweaker {
|
||||
public:
|
||||
ParticleValidationDrawableTweaker(std::string id_, bool active_, int32_t layerIndex_)
|
||||
: id(std::move(id_)),
|
||||
active(active_),
|
||||
layerIndex(layerIndex_) {}
|
||||
enum class ValidationPass : uint8_t {
|
||||
State = 0,
|
||||
Trail = 1,
|
||||
Composite = 2,
|
||||
};
|
||||
|
||||
~ParticleValidationDrawableTweaker() override {
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: validation drawable destroy id=" + id +
|
||||
" validationFrames=" + std::to_string(validationFrameCount));
|
||||
const char* slotName(const uint8_t slot) {
|
||||
return slot == 0 ? "A" : "B";
|
||||
}
|
||||
|
||||
Size validViewportSize(const Size size) {
|
||||
return {std::max<uint32_t>(1, size.width), std::max<uint32_t>(1, size.height)};
|
||||
}
|
||||
|
||||
void removeTargets(const std::vector<RenderTargetPtr>& targets, UniqueChangeRequestVec& changes) {
|
||||
for (const auto& target : targets) {
|
||||
if (target) {
|
||||
changes.emplace_back(std::make_unique<RemoveRenderTargetRequest>(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ParticleGpuValidationResources {
|
||||
public:
|
||||
uint64_t generation = 0;
|
||||
uint64_t committedFrame = 0;
|
||||
uint8_t stateRead = 0;
|
||||
uint8_t trailRead = 0;
|
||||
bool stateSeeded = false;
|
||||
bool trailSeeded = false;
|
||||
bool pending = false;
|
||||
bool contextDestroyed = false;
|
||||
Size trailSize{};
|
||||
|
||||
std::array<RenderTargetPtr, 2> stateTargets;
|
||||
std::array<RenderTargetPtr, 2> trailTargets;
|
||||
std::vector<RenderTargetPtr> registeredTargets;
|
||||
gfx::ShaderProgramBasePtr shader;
|
||||
std::shared_ptr<gfx::VertexVector<HeatmapTextureLayoutVertex>> vertices;
|
||||
SegmentVector segments;
|
||||
|
||||
uint8_t stateWrite() const { return static_cast<uint8_t>(1 - stateRead); }
|
||||
uint8_t trailWrite() const { return static_cast<uint8_t>(1 - trailRead); }
|
||||
|
||||
void resetSequence() {
|
||||
committedFrame = 0;
|
||||
stateRead = 0;
|
||||
trailRead = 0;
|
||||
stateSeeded = false;
|
||||
trailSeeded = false;
|
||||
pending = false;
|
||||
for (const auto& target : stateTargets) {
|
||||
if (target && target->getLayerGroup(0)) {
|
||||
target->getLayerGroup(0)->clearDrawables();
|
||||
}
|
||||
}
|
||||
for (const auto& target : trailTargets) {
|
||||
if (target && target->getLayerGroup(0)) {
|
||||
target->getLayerGroup(0)->clearDrawables();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void commit() {
|
||||
if (!pending) {
|
||||
return;
|
||||
}
|
||||
stateRead = stateWrite();
|
||||
trailRead = trailWrite();
|
||||
stateSeeded = true;
|
||||
trailSeeded = true;
|
||||
++committedFrame;
|
||||
pending = false;
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class ParticleGpuValidationTweaker final : public gfx::DrawableTweaker {
|
||||
public:
|
||||
ParticleGpuValidationTweaker(std::shared_ptr<ParticleGpuValidationResources> state_,
|
||||
ValidationPass pass_,
|
||||
uint8_t readSlot_,
|
||||
uint8_t writeSlot_,
|
||||
gfx::Texture2DPtr source_,
|
||||
gfx::Texture2DPtr target_)
|
||||
: state(std::move(state_)),
|
||||
pass(pass_),
|
||||
readSlot(readSlot_),
|
||||
writeSlot(writeSlot_),
|
||||
source(std::move(source_)),
|
||||
target(std::move(target_)) {}
|
||||
|
||||
void init(gfx::Drawable&) override {}
|
||||
|
||||
void execute(gfx::Drawable&, PaintParameters& parameters) override {
|
||||
if (parameters.pass != RenderPass::Translucent) {
|
||||
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
||||
auto resources = state.lock();
|
||||
if (parameters.pass != RenderPass::Translucent || !resources || !source) {
|
||||
return;
|
||||
}
|
||||
|
||||
++validationFrameCount;
|
||||
if (validationFrameCount != 1 && validationFrameCount % 60 != 0) {
|
||||
drawable.setTexture(source, shaders::idParticleGpuValidationPreviousTexture);
|
||||
|
||||
const float frame = static_cast<float>(resources->committedFrame + 1);
|
||||
const float markerX = 0.5f + 0.32f * std::sin(frame * 0.071f);
|
||||
const float markerY = 0.5f + 0.32f * std::cos(frame * 0.053f);
|
||||
const shaders::ParticleGpuValidationUBO ubo = {
|
||||
.pass = {static_cast<float>(pass), frame, 0.0f, 0.0f},
|
||||
.marker = {markerX, markerY, 0.035f, 0.965f},
|
||||
};
|
||||
drawable.mutableUniformBuffers().createOrUpdate(shaders::idParticleGpuValidationUBO, &ubo, parameters.context);
|
||||
|
||||
if (pass == ValidationPass::Composite) {
|
||||
const auto beforeState = resources->stateRead;
|
||||
const auto beforeTrail = resources->trailRead;
|
||||
resources->commit();
|
||||
if (resources->committedFrame == 1 || resources->committedFrame % 60 == 0) {
|
||||
const auto size = parameters.state.getSize();
|
||||
std::ostringstream message;
|
||||
message << "ParticleValidation30_02: commit generation=" << resources->generation
|
||||
<< " frame=" << resources->committedFrame << " state=" << slotName(beforeState) << "->"
|
||||
<< slotName(resources->stateRead) << " trail=" << slotName(beforeTrail) << "->"
|
||||
<< slotName(resources->trailRead)
|
||||
<< " order=State,Trail,Composite hazard=SAFE viewport=" << size.width << 'x' << size.height
|
||||
<< " zoom=" << parameters.state.getZoom() << " bearing=" << parameters.state.getBearing()
|
||||
<< " pitch=" << parameters.state.getPitch();
|
||||
Log::Info(Event::Render, message.str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size = parameters.state.getSize();
|
||||
const auto& matrix = parameters.transformParams.projMatrix;
|
||||
std::ostringstream message;
|
||||
message << "ParticleValidation: render frame=" << validationFrameCount << " mapFrame=" << parameters.frameCount
|
||||
<< " state=" << (active ? "ACTIVE" : "STATIC") << " repaint=" << (active ? "requested" : "stopped")
|
||||
<< " pass=translucent" << " layerIndex=" << layerIndex << " currentLayer=" << parameters.currentLayer
|
||||
<< " viewport=" << size.width << 'x' << size.height << " zoom=" << parameters.state.getZoom()
|
||||
<< " bearing=" << parameters.state.getBearing() << " pitch=" << parameters.state.getPitch() << " proj=["
|
||||
<< matrix[0] << ',' << matrix[5] << ',' << matrix[12] << ',' << matrix[13] << ']';
|
||||
Log::Info(Event::Render, message.str());
|
||||
if (source == target) {
|
||||
Log::Error(Event::Render,
|
||||
"ParticleValidation30_02: HAZARD source equals target generation=" +
|
||||
std::to_string(resources->generation));
|
||||
return;
|
||||
}
|
||||
|
||||
if (resources->committedFrame == 0 || (resources->committedFrame + 1) % 60 == 0) {
|
||||
const auto passName = pass == ValidationPass::State ? "State" : "Trail";
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation30_02: pass=" + std::string(passName) +
|
||||
" generation=" + std::to_string(resources->generation) +
|
||||
" frame=" + std::to_string(resources->committedFrame + 1) + " read=" + slotName(readSlot) +
|
||||
" write=" + slotName(writeSlot) + " distinct=true");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
bool active;
|
||||
int32_t layerIndex;
|
||||
uint64_t validationFrameCount = 0;
|
||||
std::weak_ptr<ParticleGpuValidationResources> state;
|
||||
ValidationPass pass;
|
||||
uint8_t readSlot;
|
||||
uint8_t writeSlot;
|
||||
gfx::Texture2DPtr source;
|
||||
gfx::Texture2DPtr target;
|
||||
};
|
||||
|
||||
RenderTargetPtr createValidationTarget(gfx::Context& context,
|
||||
const Size size,
|
||||
const std::string& name,
|
||||
const gfx::TextureFilterType filter) {
|
||||
auto target = context.createRenderTarget(size, gfx::TextureChannelDataType::UnsignedByte);
|
||||
if (!target || !target->getTexture()) {
|
||||
return {};
|
||||
}
|
||||
target->getTexture()->setSamplerConfiguration(
|
||||
{.filter = filter, .wrapU = gfx::TextureWrapType::Clamp, .wrapV = gfx::TextureWrapType::Clamp});
|
||||
auto group = context.createLayerGroup(0, 1, name);
|
||||
if (!group || !target->addLayerGroup(std::move(group), true)) {
|
||||
return {};
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
bool buildQuad(gfx::Context& context,
|
||||
const std::shared_ptr<ParticleGpuValidationResources>& resources,
|
||||
LayerGroup& group,
|
||||
const std::string& name,
|
||||
const gfx::Texture2DPtr& source,
|
||||
const gfx::Texture2DPtr& target,
|
||||
const ValidationPass pass,
|
||||
const uint8_t readSlot,
|
||||
const uint8_t writeSlot) {
|
||||
group.clearDrawables();
|
||||
auto attributes = context.createVertexAttributeArray();
|
||||
if (const auto& attribute = attributes->set(shaders::idParticleGpuValidationPosVertexAttribute)) {
|
||||
attribute->setSharedRawData(resources->vertices,
|
||||
offsetof(HeatmapTextureLayoutVertex, a1),
|
||||
0,
|
||||
sizeof(HeatmapTextureLayoutVertex),
|
||||
gfx::AttributeDataType::Short2);
|
||||
}
|
||||
|
||||
auto builder = context.createDrawableBuilder(name);
|
||||
builder->setShader(resources->shader);
|
||||
builder->setRenderPass(RenderPass::Translucent);
|
||||
builder->setEnableDepth(false);
|
||||
builder->setColorMode(pass == ValidationPass::Composite ? gfx::ColorMode::alphaBlended()
|
||||
: gfx::ColorMode::unblended());
|
||||
builder->setCullFaceMode(gfx::CullFaceMode::disabled());
|
||||
builder->setVertexAttributes(std::move(attributes));
|
||||
builder->setRawVertices({}, resources->vertices->elements(), gfx::AttributeDataType::Short2);
|
||||
builder->setSegments(gfx::Triangles(),
|
||||
RenderStaticData::quadTriangleIndices().vector(),
|
||||
resources->segments.data(),
|
||||
resources->segments.size());
|
||||
builder->setTexture(source, shaders::idParticleGpuValidationPreviousTexture);
|
||||
builder->addTweaker(
|
||||
std::make_shared<ParticleGpuValidationTweaker>(resources, pass, readSlot, writeSlot, source, target));
|
||||
|
||||
// DrawableBuilder owns the current drawable until flush finalizes it.
|
||||
builder->flush(context);
|
||||
auto drawables = builder->clearDrawables();
|
||||
if (drawables.size() != 1 || !drawables.front()) {
|
||||
Log::Error(Event::Render, "ParticleValidation30_02: failed to finalize drawable name=" + name);
|
||||
return false;
|
||||
}
|
||||
group.addDrawable(std::move(drawables.front()));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderParticleValidationLayer::RenderParticleValidationLayer(Immutable<style::ParticleValidationLayer::Impl> impl_)
|
||||
: RenderLayer(makeMutable<style::ParticleValidationLayerProperties>(impl_, impl_->active)) {
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: RenderLayer create id=" + getID() + " source-less=true pass=translucent");
|
||||
Log::Info(
|
||||
Event::Render,
|
||||
"ParticleValidation: RenderLayer create id=" + getID() + " source-less=true pass=translucent stage=30-02");
|
||||
}
|
||||
|
||||
RenderParticleValidationLayer::~RenderParticleValidationLayer() {
|
||||
if (gpuValidation) {
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation30_02: resources destroy generation=" + std::to_string(gpuValidation->generation) +
|
||||
" contextDestroyed=" + (gpuValidation->contextDestroyed ? "true" : "false"));
|
||||
}
|
||||
Log::Info(Event::Render, "ParticleValidation: RenderLayer destroy id=" + getID());
|
||||
}
|
||||
|
||||
@@ -86,7 +293,7 @@ void RenderParticleValidationLayer::evaluate(const PropertyEvaluationParameters&
|
||||
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: state=" + std::string(active ? "ACTIVE" : "STATIC") + " id=" + getID() +
|
||||
" layerIndex=" + std::to_string(getLayerIndex()));
|
||||
" layerIndex=" + std::to_string(getLayerIndex()) + " stage=30-02");
|
||||
}
|
||||
|
||||
bool RenderParticleValidationLayer::needsRepaint() const {
|
||||
@@ -94,9 +301,38 @@ bool RenderParticleValidationLayer::needsRepaint() const {
|
||||
return properties.active && baseImpl->visibility != style::VisibilityType::None;
|
||||
}
|
||||
|
||||
void RenderParticleValidationLayer::update(gfx::ShaderRegistry&,
|
||||
void RenderParticleValidationLayer::markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) {
|
||||
RenderLayer::markLayerRenderable(willRender, changes);
|
||||
if (!willRender && gpuValidation) {
|
||||
removeTargets(gpuValidation->registeredTargets, changes);
|
||||
gpuValidation->registeredTargets.clear();
|
||||
gpuValidation->resetSequence();
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation30_02: disabled reset generation=" + std::to_string(gpuValidation->generation));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderParticleValidationLayer::layerRemoved(UniqueChangeRequestVec& changes) {
|
||||
if (gpuValidation) {
|
||||
removeTargets(gpuValidation->registeredTargets, changes);
|
||||
gpuValidation->registeredTargets.clear();
|
||||
}
|
||||
RenderLayer::layerRemoved(changes);
|
||||
}
|
||||
|
||||
void RenderParticleValidationLayer::markContextDestroyed() {
|
||||
if (gpuValidation) {
|
||||
gpuValidation->contextDestroyed = true;
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation30_02: context lost discard generation=" +
|
||||
std::to_string(gpuValidation->generation) + " stateRead=" + slotName(gpuValidation->stateRead) +
|
||||
" trailRead=" + slotName(gpuValidation->trailRead));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderParticleValidationLayer::update(gfx::ShaderRegistry& shaders,
|
||||
gfx::Context& context,
|
||||
const TransformState&,
|
||||
const TransformState& transformState,
|
||||
const std::shared_ptr<UpdateParameters>&,
|
||||
const RenderTree&,
|
||||
UniqueChangeRequestVec& changes) {
|
||||
@@ -105,29 +341,120 @@ void RenderParticleValidationLayer::update(gfx::ShaderRegistry&,
|
||||
setLayerGroup(std::move(group), changes);
|
||||
}
|
||||
}
|
||||
if (!layerGroup) {
|
||||
if (!layerGroup || !isLayerRenderable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* group = static_cast<LayerGroup*>(layerGroup.get());
|
||||
if (group->getDrawableCount() != 0) {
|
||||
auto resources = gpuValidation;
|
||||
if (!resources) {
|
||||
auto owned = std::make_shared<ParticleGpuValidationResources>();
|
||||
owned->generation = ++nextGpuGeneration;
|
||||
owned->shader = context.getGenericShader(shaders, ShaderName);
|
||||
owned->vertices = std::make_shared<gfx::VertexVector<HeatmapTextureLayoutVertex>>(
|
||||
RenderStaticData::heatmapTextureVertices());
|
||||
owned->segments = RenderStaticData::heatmapTextureSegments();
|
||||
for (uint8_t i = 0; i < 2; ++i) {
|
||||
owned->stateTargets[i] = createValidationTarget(
|
||||
context, StateSize, getID() + "-state-" + slotName(i), gfx::TextureFilterType::Nearest);
|
||||
}
|
||||
if (!owned->shader || !owned->stateTargets[0] || !owned->stateTargets[1]) {
|
||||
Log::Error(Event::Render, "ParticleValidation30_02: State resource creation failed");
|
||||
return;
|
||||
}
|
||||
gpuValidation = std::move(owned);
|
||||
resources = gpuValidation;
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation30_02: State A/B create generation=" + std::to_string(resources->generation) +
|
||||
" size=16x16 independent=true");
|
||||
}
|
||||
|
||||
removeTargets(resources->registeredTargets, changes);
|
||||
resources->registeredTargets.clear();
|
||||
|
||||
const auto viewport = validViewportSize(transformState.getSize());
|
||||
if (!resources->trailTargets[0] || !resources->trailTargets[1] || resources->trailSize != viewport) {
|
||||
const auto previousSize = resources->trailSize;
|
||||
resources->trailTargets = {};
|
||||
for (uint8_t i = 0; i < 2; ++i) {
|
||||
resources->trailTargets[i] = createValidationTarget(
|
||||
context, viewport, getID() + "-trail-" + slotName(i), gfx::TextureFilterType::Linear);
|
||||
}
|
||||
if (!resources->trailTargets[0] || !resources->trailTargets[1]) {
|
||||
Log::Error(Event::Render, "ParticleValidation30_02: Trail resource creation failed");
|
||||
return;
|
||||
}
|
||||
resources->trailSize = viewport;
|
||||
resources->trailRead = 0;
|
||||
resources->trailSeeded = false;
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation30_02: Trail A/B " + std::string(previousSize.isEmpty() ? "create" : "resize") +
|
||||
" generation=" + std::to_string(resources->generation) +
|
||||
" old=" + std::to_string(previousSize.width) + "x" + std::to_string(previousSize.height) +
|
||||
" new=" + std::to_string(viewport.width) + "x" + std::to_string(viewport.height) +
|
||||
" StatePreserved=true");
|
||||
}
|
||||
|
||||
const auto stateWrite = resources->stateWrite();
|
||||
const auto trailWrite = resources->trailWrite();
|
||||
auto& stateGroup = static_cast<LayerGroup&>(*resources->stateTargets[stateWrite]->getLayerGroup(0));
|
||||
auto& trailGroup = static_cast<LayerGroup&>(*resources->trailTargets[trailWrite]->getLayerGroup(0));
|
||||
auto& compositeGroup = static_cast<LayerGroup&>(*layerGroup);
|
||||
|
||||
const bool stateBuilt = buildQuad(context,
|
||||
resources,
|
||||
stateGroup,
|
||||
getID() + "-state-pass",
|
||||
resources->stateTargets[resources->stateRead]->getTexture(),
|
||||
resources->stateTargets[stateWrite]->getTexture(),
|
||||
ValidationPass::State,
|
||||
resources->stateRead,
|
||||
stateWrite);
|
||||
const bool trailBuilt = buildQuad(context,
|
||||
resources,
|
||||
trailGroup,
|
||||
getID() + "-trail-pass",
|
||||
resources->trailTargets[resources->trailRead]->getTexture(),
|
||||
resources->trailTargets[trailWrite]->getTexture(),
|
||||
ValidationPass::Trail,
|
||||
resources->trailRead,
|
||||
trailWrite);
|
||||
const bool compositeBuilt = buildQuad(context,
|
||||
resources,
|
||||
compositeGroup,
|
||||
getID() + "-composite-pass",
|
||||
resources->trailTargets[trailWrite]->getTexture(),
|
||||
{},
|
||||
ValidationPass::Composite,
|
||||
resources->trailRead,
|
||||
trailWrite);
|
||||
if (!stateBuilt || !trailBuilt || !compositeBuilt) {
|
||||
compositeGroup.clearDrawables();
|
||||
return;
|
||||
}
|
||||
|
||||
const bool active = static_cast<const style::ParticleValidationLayerProperties&>(*evaluatedProperties).active;
|
||||
auto builder = context.createDrawableBuilder(getID());
|
||||
auto& drawable = builder->getCurrentDrawable(true);
|
||||
drawable->setIsCustom(true);
|
||||
drawable->setRenderPass(RenderPass::Translucent);
|
||||
drawable->addTweaker(std::make_shared<ParticleValidationDrawableTweaker>(getID(), active, getLayerIndex()));
|
||||
builder->flush(context);
|
||||
auto drawables = builder->clearDrawables();
|
||||
group->addDrawable(std::move(drawables.front()));
|
||||
++stats.drawablesAdded;
|
||||
// Read targets are registered only for their one-time clear. Normal frames register
|
||||
// only the opposite write targets, in dependency order, so a sampled texture can
|
||||
// never be attached to the active render pass.
|
||||
if (!resources->stateSeeded) {
|
||||
resources->registeredTargets.emplace_back(resources->stateTargets[resources->stateRead]);
|
||||
}
|
||||
resources->registeredTargets.emplace_back(resources->stateTargets[stateWrite]);
|
||||
if (!resources->trailSeeded) {
|
||||
resources->registeredTargets.emplace_back(resources->trailTargets[resources->trailRead]);
|
||||
}
|
||||
resources->registeredTargets.emplace_back(resources->trailTargets[trailWrite]);
|
||||
for (const auto& target : resources->registeredTargets) {
|
||||
changes.emplace_back(std::make_unique<AddRenderTargetRequest>(target));
|
||||
}
|
||||
resources->pending = true;
|
||||
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: validation drawable create id=" + getID() +
|
||||
" layerIndex=" + std::to_string(getLayerIndex()) + " pass=translucent");
|
||||
if (resources->committedFrame == 0 || (resources->committedFrame + 1) % 60 == 0) {
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation30_02: schedule generation=" + std::to_string(resources->generation) + " frame=" +
|
||||
std::to_string(resources->committedFrame + 1) + " state=" + slotName(resources->stateRead) +
|
||||
"->" + slotName(stateWrite) + " trail=" + slotName(resources->trailRead) + "->" +
|
||||
slotName(trailWrite) + " targetsDistinct=true cpuStateCopy=false");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class ParticleGpuValidationResources;
|
||||
|
||||
// VALIDATION ONLY: proves Core RenderLayer/frame-transform/repaint integration.
|
||||
class RenderParticleValidationLayer final : public RenderLayer {
|
||||
public:
|
||||
@@ -25,6 +27,11 @@ private:
|
||||
bool hasCrossfade() const override { return false; }
|
||||
bool needsRepaint() const override;
|
||||
void prepare(const LayerPrepareParameters&) override {}
|
||||
void markLayerRenderable(bool, UniqueChangeRequestVec&) override;
|
||||
void layerRemoved(UniqueChangeRequestVec&) override;
|
||||
void markContextDestroyed() override;
|
||||
|
||||
std::shared_ptr<ParticleGpuValidationResources> gpuValidation;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -287,6 +287,19 @@ const std::vector<TextureInfo> HeatmapTextureShaderInfo::textures = {
|
||||
TextureInfo{"u_color_ramp", idHeatmapColorRampTexture},
|
||||
};
|
||||
|
||||
// Particle GPU Validation (30-02, OpenGL validation only)
|
||||
using ParticleGpuValidationShaderInfo = ShaderInfo<BuiltIn::ParticleGpuValidationShader, gfx::Backend::Type::OpenGL>;
|
||||
|
||||
const std::vector<UniformBlockInfo> ParticleGpuValidationShaderInfo::uniformBlocks = {
|
||||
UniformBlockInfo{"ParticleGpuValidationUBO", idParticleGpuValidationUBO},
|
||||
};
|
||||
const std::vector<AttributeInfo> ParticleGpuValidationShaderInfo::attributes = {
|
||||
AttributeInfo{"a_pos", idParticleGpuValidationPosVertexAttribute},
|
||||
};
|
||||
const std::vector<TextureInfo> ParticleGpuValidationShaderInfo::textures = {
|
||||
TextureInfo{"u_previous", idParticleGpuValidationPreviousTexture},
|
||||
};
|
||||
|
||||
// Hillshade Prepare
|
||||
using HillshadePrepareShaderInfo = ShaderInfo<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::OpenGL>;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ MBGL_DEFINE_ENUM(BuiltIn,
|
||||
{BuiltIn::FillExtrusionPatternInstancedShader, "FillExtrusionPatternInstancedShader"},
|
||||
{BuiltIn::HeatmapShader, "HeatmapShader"},
|
||||
{BuiltIn::HeatmapTextureShader, "HeatmapTextureShader"},
|
||||
{BuiltIn::ParticleGpuValidationShader, "ParticleGpuValidationShader"},
|
||||
{BuiltIn::HillshadePrepareShader, "HillshadePrepareShader"},
|
||||
{BuiltIn::ColorReliefShader, "ColorReliefShader"},
|
||||
{BuiltIn::HillshadeShader, "HillshadeShader"},
|
||||
|
||||
Reference in New Issue
Block a user