IS_CULLED_FLAG, IS_UNLIT_FLAG, HAS_DEPTH_BIAS_FLAG, NUM_FLAGS, }; enum Flag { IS_TEXTURED = (1 << IS_TEXTURED_FLAG), IS_CULLED = (1 << IS_CULLED_FLAG), IS_UNLIT = (1 << IS_UNLIT_FLAG), HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), }; typedef unsigned short Flags; bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } bool isTextured() const { return isFlag(IS_TEXTURED); } bool isCulled() const { return isFlag(IS_CULLED); } bool isUnlit() const { return isFlag(IS_UNLIT); } bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } Flags _flags = 0; short _spare = 0; int getRaw() const { return *reinterpret_cast<const int*>(this); } SimpleProgramKey(bool textured = false, bool culled = true, bool unlit = false, bool depthBias = false) { _flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) | (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0); } SimpleProgramKey(int bitmask) : _flags(bitmask) {}}; inline uint qHash(const SimpleProgramKey& key, uint seed) { return qHash(key.getRaw(), seed);} inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw();} void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool unlit, bool depthBiased) { batch.setPipeline(getSimplePipeline(textured, culled, unlit, depthBiased)); // If not textured, set a default albedo map if (!textured) { batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, DependencyManager::get<TextureCache>()->getWhiteTexture()); } // Set a default normal map batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, DependencyManager::get<TextureCache>()->getNormalFittingTexture());} gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled, bool unlit, bool depthBiased) { SimpleProgramKey config{ textured, culled, unlit, depthBiased }; // Compile the shaders static std::once_flag once; std::call_once(once, [&]() { auto VS = gpu::Shader::createVertex(std::string(simple_vert)); auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag)); auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); _simpleShader = gpu::Shader::createProgram(VS, PS); _unlitShader = gpu::Shader::createProgram(VS, PSUnlit); gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING)); gpu::Shader::makeProgram(*_simpleShader, slotBindings); gpu::Shader::makeProgram(*_unlitShader, slotBindings); }); // If the pipeline already exists, return it auto it = _simplePrograms.find(config); if (it != _simplePrograms.end()) { return it.value(); } // If the pipeline did not exist, make it auto state = std::make_shared<gpu::State>(); if (config.isCulled()) { state->setCullMode(gpu::State::CULL_BACK); } else { state->setCullMode(gpu::State::CULL_NONE); } state->setDepthTest(true, true, gpu::LESS_EQUAL); if (config.hasDepthBias()) { state->setDepthBias(1.0f); state->setDepthBiasSlopeScale(1.0f); } state->setBlendFunction(false, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); gpu::ShaderPointer program = (config.isUnlit()) ? _unlitShader : _simpleShader; gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state); _simplePrograms.insert(config, pipeline); return pipeline;} uint32_t toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | ((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); return compactColor;} static const size_t INSTANCE_COLOR_BUFFER = 0; void renderInstances(gpu::Batch& batch, const glm::vec4& color, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); auto compactColor = toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add call to named buffer batch.setupNamedCalls(instanceName, [isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch); if (isWire) { DependencyManager::get<GeometryCache>()->renderWireShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } else { DependencyManager::get<GeometryCache>()->renderShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } });} void GeometryCache::renderSolidShapeInstance(gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { renderInstances(batch, color, false, pipeline, shape);} void GeometryCache::renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { renderInstances(batch, color, false, pipeline, GeometryCache::Sphere);} void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { renderInstances(batch, color, true, pipeline, GeometryCache::Sphere);} // Enable this in a debug build to cause 'box' entities to iterate through all the// available shape types, both solid and wireframes//#define DEBUG_SHAPES void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {#ifdef DEBUG_SHAPES static auto startTime = usecTimestampNow(); renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { auto usecs = usecTimestampNow(); usecs -= startTime; auto msecs = usecs / USECS_PER_MSEC; float seconds = msecs; seconds /= MSECS_PER_SECOND; float fractionalSeconds = seconds - floor(seconds); int shapeIndex = (int)seconds; // Every second we flip to the next shape. static const int SHAPE_COUNT = 5; GeometryCache::Shape shapes[SHAPE_COUNT] = { GeometryCache::Cube, GeometryCache::Tetrahedron, GeometryCache::Sphere, GeometryCache::Icosahedron, GeometryCache::Line, }; shapeIndex %= SHAPE_COUNT; GeometryCache::Shape shape = shapes[shapeIndex]; // For the first half second for a given shape, show the wireframe, for the second half, show the solid. if (fractionalSeconds > 0.5f) { renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape); } else { renderInstances(INSTANCE_NAME, batch, color, false, pipeline, shape); } });#else renderInstances(batch, color, false, pipeline, GeometryCache::Cube);#endif} void GeometryCache::renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { static const std::string INSTANCE_NAME = __FUNCTION__; renderInstances(batch, color, true, pipeline, GeometryCache::Cube);}Lines 1501–1698 of 1698. Previous lines
IS_CULLED_FLAG, IS_UNLIT_FLAG, HAS_DEPTH_BIAS_FLAG, NUM_FLAGS, }; enum Flag { IS_TEXTURED = (1 << IS_TEXTURED_FLAG), IS_CULLED = (1 << IS_CULLED_FLAG), IS_UNLIT = (1 << IS_UNLIT_FLAG), HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), }; typedef unsigned short Flags; bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } bool isTextured() const { return isFlag(IS_TEXTURED); } bool isCulled() const { return isFlag(IS_CULLED); } bool isUnlit() const { return isFlag(IS_UNLIT); } bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } Flags _flags = 0; short _spare = 0; int getRaw() const { return *reinterpret_cast<const int*>(this); } SimpleProgramKey(bool textured = false, bool culled = true, bool unlit = false, bool depthBias = false) { _flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) | (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0); } SimpleProgramKey(int bitmask) : _flags(bitmask) {} }; inline uint qHash(const SimpleProgramKey& key, uint seed) { return qHash(key.getRaw(), seed); } inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool unlit, bool depthBiased) { batch.setPipeline(getSimplePipeline(textured, culled, unlit, depthBiased)); // If not textured, set a default albedo map if (!textured) { batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, DependencyManager::get<TextureCache>()->getWhiteTexture()); } // Set a default normal map batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, DependencyManager::get<TextureCache>()->getNormalFittingTexture()); } gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled, bool unlit, bool depthBiased) { SimpleProgramKey config{ textured, culled, unlit, depthBiased }; // Compile the shaders static std::once_flag once; std::call_once(once, [&]() { auto VS = gpu::Shader::createVertex(std::string(simple_vert)); auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag)); auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); _simpleShader = gpu::Shader::createProgram(VS, PS); _unlitShader = gpu::Shader::createProgram(VS, PSUnlit); gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING)); gpu::Shader::makeProgram(*_simpleShader, slotBindings); gpu::Shader::makeProgram(*_unlitShader, slotBindings); }); // If the pipeline already exists, return it auto it = _simplePrograms.find(config); if (it != _simplePrograms.end()) { return it.value(); } // If the pipeline did not exist, make it auto state = std::make_shared<gpu::State>(); if (config.isCulled()) { state->setCullMode(gpu::State::CULL_BACK); } else { state->setCullMode(gpu::State::CULL_NONE); } state->setDepthTest(true, true, gpu::LESS_EQUAL); if (config.hasDepthBias()) { state->setDepthBias(1.0f); state->setDepthBiasSlopeScale(1.0f); } state->setBlendFunction(false, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); gpu::ShaderPointer program = (config.isUnlit()) ? _unlitShader : _simpleShader; gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state); _simplePrograms.insert(config, pipeline); return pipeline; } uint32_t toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | ((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); return compactColor; } static const size_t INSTANCE_COLOR_BUFFER = 0; void renderInstances(gpu::Batch& batch, const glm::vec4& color, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); auto compactColor = toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add call to named buffer batch.setupNamedCalls(instanceName, [isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch); if (isWire) { DependencyManager::get<GeometryCache>()->renderWireShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } else { DependencyManager::get<GeometryCache>()->renderShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } }); } void GeometryCache::renderSolidShapeInstance(gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { renderInstances(batch, color, false, pipeline, shape); } void GeometryCache::renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { renderInstances(batch, color, false, pipeline, GeometryCache::Sphere); } void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { renderInstances(batch, color, true, pipeline, GeometryCache::Sphere); } // Enable this in a debug build to cause 'box' entities to iterate through all the // available shape types, both solid and wireframes //#define DEBUG_SHAPES void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { #ifdef DEBUG_SHAPES static auto startTime = usecTimestampNow(); renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { auto usecs = usecTimestampNow(); usecs -= startTime; auto msecs = usecs / USECS_PER_MSEC; float seconds = msecs; seconds /= MSECS_PER_SECOND; float fractionalSeconds = seconds - floor(seconds); int shapeIndex = (int)seconds; // Every second we flip to the next shape. static const int SHAPE_COUNT = 5; GeometryCache::Shape shapes[SHAPE_COUNT] = { GeometryCache::Cube, GeometryCache::Tetrahedron, GeometryCache::Sphere, GeometryCache::Icosahedron, GeometryCache::Line, }; shapeIndex %= SHAPE_COUNT; GeometryCache::Shape shape = shapes[shapeIndex]; // For the first half second for a given shape, show the wireframe, for the second half, show the solid. if (fractionalSeconds > 0.5f) { renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape); } else { renderInstances(INSTANCE_NAME, batch, color, false, pipeline, shape); } }); #else renderInstances(batch, color, false, pipeline, GeometryCache::Cube); #endif } void GeometryCache::renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { static const std::string INSTANCE_NAME = __FUNCTION__; renderInstances(batch, color, true, pipeline, GeometryCache::Cube); }