| /*------------------------------------------------------------------------- |
| * drawElements Quality Program Reference Renderer |
| * ----------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Reference renderer interface. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "rrRenderer.hpp" |
| #include "tcuVectorUtil.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuFloat.hpp" |
| #include "rrPrimitiveAssembler.hpp" |
| #include "rrFragmentOperations.hpp" |
| #include "rrRasterizer.hpp" |
| #include "deMemory.h" |
| |
| #include <set> |
| #include <limits> |
| |
| namespace rr |
| { |
| namespace |
| { |
| |
| typedef double ClipFloat; // floating point type used in clipping |
| |
| typedef tcu::Vector<ClipFloat, 4> ClipVec4; |
| |
| struct RasterizationInternalBuffers |
| { |
| std::vector<FragmentPacket> fragmentPackets; |
| std::vector<GenericVec4> shaderOutputs; |
| std::vector<GenericVec4> shaderOutputsSrc1; |
| std::vector<Fragment> shadedFragments; |
| float *fragmentDepthBuffer; |
| }; |
| |
| uint32_t readIndexArray(const IndexType type, const void *ptr, size_t ndx) |
| { |
| switch (type) |
| { |
| case INDEXTYPE_UINT8: |
| return ((const uint8_t *)ptr)[ndx]; |
| |
| case INDEXTYPE_UINT16: |
| { |
| uint16_t retVal; |
| deMemcpy(&retVal, (const uint8_t *)ptr + ndx * sizeof(uint16_t), sizeof(uint16_t)); |
| |
| return retVal; |
| } |
| |
| case INDEXTYPE_UINT32: |
| { |
| uint32_t retVal; |
| deMemcpy(&retVal, (const uint8_t *)ptr + ndx * sizeof(uint32_t), sizeof(uint32_t)); |
| |
| return retVal; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| tcu::IVec4 getBufferSize(const rr::MultisampleConstPixelBufferAccess &multisampleBuffer) |
| { |
| return tcu::IVec4(0, 0, multisampleBuffer.raw().getHeight(), multisampleBuffer.raw().getDepth()); |
| } |
| |
| bool isEmpty(const rr::MultisampleConstPixelBufferAccess &access) |
| { |
| return access.raw().getWidth() == 0 || access.raw().getHeight() == 0 || access.raw().getDepth() == 0; |
| } |
| |
| struct DrawContext |
| { |
| int primitiveID; |
| |
| DrawContext(void) : primitiveID(0) |
| { |
| } |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Calculates intersection of two rects given as (left, bottom, width, height) |
| *//*--------------------------------------------------------------------*/ |
| tcu::IVec4 rectIntersection(const tcu::IVec4 &a, const tcu::IVec4 &b) |
| { |
| const tcu::IVec2 pos = tcu::IVec2(de::max(a.x(), b.x()), de::max(a.y(), b.y())); |
| const tcu::IVec2 endPos = tcu::IVec2(de::min(a.x() + a.z(), b.x() + b.z()), de::min(a.y() + a.w(), b.y() + b.w())); |
| |
| return tcu::IVec4(pos.x(), pos.y(), endPos.x() - pos.x(), endPos.y() - pos.y()); |
| } |
| |
| void convertPrimitiveToBaseType(std::vector<pa::Triangle> &output, std::vector<pa::Triangle> &input) |
| { |
| std::swap(output, input); |
| } |
| |
| void convertPrimitiveToBaseType(std::vector<pa::Line> &output, std::vector<pa::Line> &input) |
| { |
| std::swap(output, input); |
| } |
| |
| void convertPrimitiveToBaseType(std::vector<pa::Point> &output, std::vector<pa::Point> &input) |
| { |
| std::swap(output, input); |
| } |
| |
| void convertPrimitiveToBaseType(std::vector<pa::Line> &output, std::vector<pa::LineAdjacency> &input) |
| { |
| output.resize(input.size()); |
| for (size_t i = 0; i < input.size(); ++i) |
| { |
| const int adjacentProvokingVertex = input[i].provokingIndex; |
| const int baseProvokingVertexIndex = adjacentProvokingVertex - 1; |
| output[i] = pa::Line(input[i].v1, input[i].v2, baseProvokingVertexIndex); |
| } |
| } |
| |
| void convertPrimitiveToBaseType(std::vector<pa::Triangle> &output, std::vector<pa::TriangleAdjacency> &input) |
| { |
| output.resize(input.size()); |
| for (size_t i = 0; i < input.size(); ++i) |
| { |
| const int adjacentProvokingVertex = input[i].provokingIndex; |
| const int baseProvokingVertexIndex = adjacentProvokingVertex / 2; |
| output[i] = pa::Triangle(input[i].v0, input[i].v2, input[i].v4, baseProvokingVertexIndex); |
| } |
| } |
| |
| namespace cliputil |
| { |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Get clipped portion of the second endpoint |
| * |
| * Calculate the intersection of line segment v0-v1 and a given plane. Line |
| * segment is defined by a pair of one-dimensional homogeneous coordinates. |
| * |
| *//*--------------------------------------------------------------------*/ |
| ClipFloat getSegmentVolumeEdgeClip(const ClipFloat v0, const ClipFloat w0, const ClipFloat v1, const ClipFloat w1, |
| const ClipFloat plane) |
| { |
| // The +epsilon avoids division by zero without causing a meaningful change in the calculation. |
| // Fixes divide by zero in builds when using the gcc toolset. |
| return (plane * w0 - v0) / ((v1 - v0) - plane * (w1 - w0) + std::numeric_limits<ClipFloat>::epsilon()); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Get clipped portion of the endpoint |
| * |
| * How much (in [0-1] range) of a line segment v0-v1 would be clipped |
| * of the v0 end of the line segment by clipping. |
| *//*--------------------------------------------------------------------*/ |
| ClipFloat getLineEndpointClipping(const ClipVec4 &v0, const ClipVec4 &v1) |
| { |
| const ClipFloat clipVolumeSize = (ClipFloat)1.0; |
| |
| if (v0.z() > v0.w()) |
| { |
| // Clip +Z |
| return getSegmentVolumeEdgeClip(v0.z(), v0.w(), v1.z(), v1.w(), clipVolumeSize); |
| } |
| else if (v0.z() < -v0.w()) |
| { |
| // Clip -Z |
| return getSegmentVolumeEdgeClip(v0.z(), v0.w(), v1.z(), v1.w(), -clipVolumeSize); |
| } |
| else |
| { |
| // no clipping |
| return (ClipFloat)0.0; |
| } |
| } |
| |
| ClipVec4 vec4ToClipVec4(const tcu::Vec4 &v) |
| { |
| return ClipVec4((ClipFloat)v.x(), (ClipFloat)v.y(), (ClipFloat)v.z(), (ClipFloat)v.w()); |
| } |
| |
| tcu::Vec4 clipVec4ToVec4(const ClipVec4 &v) |
| { |
| return tcu::Vec4((float)v.x(), (float)v.y(), (float)v.z(), (float)v.w()); |
| } |
| |
| class ClipVolumePlane |
| { |
| public: |
| virtual ~ClipVolumePlane() |
| { |
| } |
| virtual bool pointInClipVolume(const ClipVec4 &p) const = 0; |
| virtual ClipFloat clipLineSegmentEnd(const ClipVec4 &v0, const ClipVec4 &v1) const = 0; |
| virtual ClipVec4 getLineIntersectionPoint(const ClipVec4 &v0, const ClipVec4 &v1) const = 0; |
| }; |
| |
| template <int Sign, int CompNdx> |
| class ComponentPlane : public ClipVolumePlane |
| { |
| DE_STATIC_ASSERT(Sign == +1 || Sign == -1); |
| |
| public: |
| bool pointInClipVolume(const ClipVec4 &p) const; |
| ClipFloat clipLineSegmentEnd(const ClipVec4 &v0, const ClipVec4 &v1) const; |
| ClipVec4 getLineIntersectionPoint(const ClipVec4 &v0, const ClipVec4 &v1) const; |
| }; |
| |
| template <int Sign, int CompNdx> |
| bool ComponentPlane<Sign, CompNdx>::pointInClipVolume(const ClipVec4 &p) const |
| { |
| const ClipFloat clipVolumeSize = (ClipFloat)1.0; |
| |
| return (ClipFloat)(Sign * p[CompNdx]) <= clipVolumeSize * p.w(); |
| } |
| |
| template <int Sign, int CompNdx> |
| ClipFloat ComponentPlane<Sign, CompNdx>::clipLineSegmentEnd(const ClipVec4 &v0, const ClipVec4 &v1) const |
| { |
| const ClipFloat clipVolumeSize = (ClipFloat)1.0; |
| |
| return getSegmentVolumeEdgeClip(v0[CompNdx], v0.w(), v1[CompNdx], v1.w(), (ClipFloat)Sign * clipVolumeSize); |
| } |
| |
| template <int Sign, int CompNdx> |
| ClipVec4 ComponentPlane<Sign, CompNdx>::getLineIntersectionPoint(const ClipVec4 &v0, const ClipVec4 &v1) const |
| { |
| // A point on line might be far away, causing clipping ratio (clipLineSegmentEnd) to become extremely close to 1.0 |
| // even if the another point is not on the plane. Prevent clipping ratio from saturating by using points on line |
| // that are (nearly) on this and (nearly) on the opposite plane. |
| |
| const ClipVec4 clippedV0 = tcu::mix(v0, v1, ComponentPlane<+1, CompNdx>().clipLineSegmentEnd(v0, v1)); |
| const ClipVec4 clippedV1 = tcu::mix(v0, v1, ComponentPlane<-1, CompNdx>().clipLineSegmentEnd(v0, v1)); |
| const ClipFloat clipRatio = clipLineSegmentEnd(clippedV0, clippedV1); |
| |
| // Find intersection point of line from v0 to v1 and the current plane. Avoid ratios near 1.0 |
| if (clipRatio <= (ClipFloat)0.5) |
| return tcu::mix(clippedV0, clippedV1, clipRatio); |
| else |
| { |
| const ClipFloat complementClipRatio = clipLineSegmentEnd(clippedV1, clippedV0); |
| return tcu::mix(clippedV1, clippedV0, complementClipRatio); |
| } |
| } |
| |
| struct TriangleVertex |
| { |
| ClipVec4 position; |
| ClipFloat weight[3]; //!< barycentrics |
| }; |
| |
| struct SubTriangle |
| { |
| TriangleVertex vertices[3]; |
| }; |
| |
| void clipTriangleOneVertex(std::vector<TriangleVertex> &clippedEdges, const ClipVolumePlane &plane, |
| const TriangleVertex &clipped, const TriangleVertex &v1, const TriangleVertex &v2) |
| { |
| const ClipFloat degenerateLimit = (ClipFloat)1.0; |
| |
| // calc clip pos |
| TriangleVertex mid1; |
| TriangleVertex mid2; |
| bool outputDegenerate = false; |
| |
| { |
| const TriangleVertex &inside = v1; |
| const TriangleVertex &outside = clipped; |
| TriangleVertex &middle = mid1; |
| |
| const ClipFloat hitDist = plane.clipLineSegmentEnd(inside.position, outside.position); |
| |
| if (hitDist >= degenerateLimit) |
| { |
| // do not generate degenerate triangles |
| outputDegenerate = true; |
| } |
| else |
| { |
| const ClipVec4 approximatedClipPoint = tcu::mix(inside.position, outside.position, hitDist); |
| const ClipVec4 anotherPointOnLine = (hitDist > (ClipFloat)0.5) ? (inside.position) : (outside.position); |
| |
| middle.position = plane.getLineIntersectionPoint(approximatedClipPoint, anotherPointOnLine); |
| middle.weight[0] = tcu::mix(inside.weight[0], outside.weight[0], hitDist); |
| middle.weight[1] = tcu::mix(inside.weight[1], outside.weight[1], hitDist); |
| middle.weight[2] = tcu::mix(inside.weight[2], outside.weight[2], hitDist); |
| } |
| } |
| |
| { |
| const TriangleVertex &inside = v2; |
| const TriangleVertex &outside = clipped; |
| TriangleVertex &middle = mid2; |
| |
| const ClipFloat hitDist = plane.clipLineSegmentEnd(inside.position, outside.position); |
| |
| if (hitDist >= degenerateLimit) |
| { |
| // do not generate degenerate triangles |
| outputDegenerate = true; |
| } |
| else |
| { |
| const ClipVec4 approximatedClipPoint = tcu::mix(inside.position, outside.position, hitDist); |
| const ClipVec4 anotherPointOnLine = (hitDist > (ClipFloat)0.5) ? (inside.position) : (outside.position); |
| |
| middle.position = plane.getLineIntersectionPoint(approximatedClipPoint, anotherPointOnLine); |
| middle.weight[0] = tcu::mix(inside.weight[0], outside.weight[0], hitDist); |
| middle.weight[1] = tcu::mix(inside.weight[1], outside.weight[1], hitDist); |
| middle.weight[2] = tcu::mix(inside.weight[2], outside.weight[2], hitDist); |
| } |
| } |
| |
| if (!outputDegenerate) |
| { |
| // gen quad (v1) -> mid1 -> mid2 -> (v2) |
| clippedEdges.push_back(v1); |
| clippedEdges.push_back(mid1); |
| clippedEdges.push_back(mid2); |
| clippedEdges.push_back(v2); |
| } |
| else |
| { |
| // don't modify |
| clippedEdges.push_back(v1); |
| clippedEdges.push_back(clipped); |
| clippedEdges.push_back(v2); |
| } |
| } |
| |
| void clipTriangleTwoVertices(std::vector<TriangleVertex> &clippedEdges, const ClipVolumePlane &plane, |
| const TriangleVertex &v0, const TriangleVertex &clipped1, const TriangleVertex &clipped2) |
| { |
| const ClipFloat unclippableLimit = (ClipFloat)1.0; |
| |
| // calc clip pos |
| TriangleVertex mid1; |
| TriangleVertex mid2; |
| bool unclippableVertex1 = false; |
| bool unclippableVertex2 = false; |
| |
| { |
| const TriangleVertex &inside = v0; |
| const TriangleVertex &outside = clipped1; |
| TriangleVertex &middle = mid1; |
| |
| const ClipFloat hitDist = plane.clipLineSegmentEnd(inside.position, outside.position); |
| |
| if (hitDist >= unclippableLimit) |
| { |
| // this edge cannot be clipped because the edge is really close to the volume boundary |
| unclippableVertex1 = true; |
| } |
| else |
| { |
| const ClipVec4 approximatedClipPoint = tcu::mix(inside.position, outside.position, hitDist); |
| const ClipVec4 anotherPointOnLine = (hitDist > (ClipFloat)0.5) ? (inside.position) : (outside.position); |
| |
| middle.position = plane.getLineIntersectionPoint(approximatedClipPoint, anotherPointOnLine); |
| middle.weight[0] = tcu::mix(inside.weight[0], outside.weight[0], hitDist); |
| middle.weight[1] = tcu::mix(inside.weight[1], outside.weight[1], hitDist); |
| middle.weight[2] = tcu::mix(inside.weight[2], outside.weight[2], hitDist); |
| } |
| } |
| |
| { |
| const TriangleVertex &inside = v0; |
| const TriangleVertex &outside = clipped2; |
| TriangleVertex &middle = mid2; |
| |
| const ClipFloat hitDist = plane.clipLineSegmentEnd(inside.position, outside.position); |
| |
| if (hitDist >= unclippableLimit) |
| { |
| // this edge cannot be clipped because the edge is really close to the volume boundary |
| unclippableVertex2 = true; |
| } |
| else |
| { |
| const ClipVec4 approximatedClipPoint = tcu::mix(inside.position, outside.position, hitDist); |
| const ClipVec4 anotherPointOnLine = (hitDist > (ClipFloat)0.5) ? (inside.position) : (outside.position); |
| |
| middle.position = plane.getLineIntersectionPoint(approximatedClipPoint, anotherPointOnLine); |
| middle.weight[0] = tcu::mix(inside.weight[0], outside.weight[0], hitDist); |
| middle.weight[1] = tcu::mix(inside.weight[1], outside.weight[1], hitDist); |
| middle.weight[2] = tcu::mix(inside.weight[2], outside.weight[2], hitDist); |
| } |
| } |
| |
| if (!unclippableVertex1 && !unclippableVertex2) |
| { |
| // gen triangle (v0) -> mid1 -> mid2 |
| clippedEdges.push_back(v0); |
| clippedEdges.push_back(mid1); |
| clippedEdges.push_back(mid2); |
| } |
| else if (!unclippableVertex1 && unclippableVertex2) |
| { |
| // clip just vertex 1 |
| clippedEdges.push_back(v0); |
| clippedEdges.push_back(mid1); |
| clippedEdges.push_back(clipped2); |
| } |
| else if (unclippableVertex1 && !unclippableVertex2) |
| { |
| // clip just vertex 2 |
| clippedEdges.push_back(v0); |
| clippedEdges.push_back(clipped1); |
| clippedEdges.push_back(mid2); |
| } |
| else |
| { |
| // don't modify |
| clippedEdges.push_back(v0); |
| clippedEdges.push_back(clipped1); |
| clippedEdges.push_back(clipped2); |
| } |
| } |
| |
| void clipTriangleToPlane(std::vector<TriangleVertex> &clippedEdges, const TriangleVertex *vertices, |
| const ClipVolumePlane &plane) |
| { |
| const bool v0Clipped = !plane.pointInClipVolume(vertices[0].position); |
| const bool v1Clipped = !plane.pointInClipVolume(vertices[1].position); |
| const bool v2Clipped = !plane.pointInClipVolume(vertices[2].position); |
| const int clipCount = ((v0Clipped) ? (1) : (0)) + ((v1Clipped) ? (1) : (0)) + ((v2Clipped) ? (1) : (0)); |
| |
| if (clipCount == 0) |
| { |
| // pass |
| clippedEdges.insert(clippedEdges.begin(), vertices, vertices + 3); |
| } |
| else if (clipCount == 1) |
| { |
| // clip one vertex |
| if (v0Clipped) |
| clipTriangleOneVertex(clippedEdges, plane, vertices[0], vertices[1], vertices[2]); |
| else if (v1Clipped) |
| clipTriangleOneVertex(clippedEdges, plane, vertices[1], vertices[2], vertices[0]); |
| else |
| clipTriangleOneVertex(clippedEdges, plane, vertices[2], vertices[0], vertices[1]); |
| } |
| else if (clipCount == 2) |
| { |
| // clip two vertices |
| if (!v0Clipped) |
| clipTriangleTwoVertices(clippedEdges, plane, vertices[0], vertices[1], vertices[2]); |
| else if (!v1Clipped) |
| clipTriangleTwoVertices(clippedEdges, plane, vertices[1], vertices[2], vertices[0]); |
| else |
| clipTriangleTwoVertices(clippedEdges, plane, vertices[2], vertices[0], vertices[1]); |
| } |
| else if (clipCount == 3) |
| { |
| // discard |
| } |
| else |
| { |
| DE_ASSERT(false); |
| } |
| } |
| |
| } // namespace cliputil |
| |
| tcu::Vec2 to2DCartesian(const tcu::Vec4 &p) |
| { |
| return tcu::Vec2(p.x(), p.y()) / p.w(); |
| } |
| |
| float cross2D(const tcu::Vec2 &a, const tcu::Vec2 &b) |
| { |
| return tcu::cross(tcu::Vec3(a.x(), a.y(), 0.0f), tcu::Vec3(b.x(), b.y(), 0.0f)).z(); |
| } |
| |
| void flatshadePrimitiveVertices(pa::Triangle &target, size_t outputNdx) |
| { |
| const rr::GenericVec4 flatValue = target.getProvokingVertex()->outputs[outputNdx]; |
| target.v0->outputs[outputNdx] = flatValue; |
| target.v1->outputs[outputNdx] = flatValue; |
| target.v2->outputs[outputNdx] = flatValue; |
| } |
| |
| void flatshadePrimitiveVertices(pa::Line &target, size_t outputNdx) |
| { |
| const rr::GenericVec4 flatValue = target.getProvokingVertex()->outputs[outputNdx]; |
| target.v0->outputs[outputNdx] = flatValue; |
| target.v1->outputs[outputNdx] = flatValue; |
| } |
| |
| void flatshadePrimitiveVertices(pa::Point &target, size_t outputNdx) |
| { |
| DE_UNREF(target); |
| DE_UNREF(outputNdx); |
| } |
| |
| template <typename ContainerType> |
| void flatshadeVertices(const Program &program, ContainerType &list) |
| { |
| // flatshade |
| const std::vector<rr::VertexVaryingInfo> &fragInputs = |
| (program.geometryShader) ? (program.geometryShader->getOutputs()) : (program.vertexShader->getOutputs()); |
| |
| for (size_t inputNdx = 0; inputNdx < fragInputs.size(); ++inputNdx) |
| if (fragInputs[inputNdx].flatshade) |
| for (typename ContainerType::iterator it = list.begin(); it != list.end(); ++it) |
| flatshadePrimitiveVertices(*it, inputNdx); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * Clip triangles to the clip volume. |
| *//*--------------------------------------------------------------------*/ |
| void clipPrimitives(std::vector<pa::Triangle> &list, const Program &program, bool clipWithZPlanes, |
| VertexPacketAllocator &vpalloc) |
| { |
| using namespace cliputil; |
| |
| cliputil::ComponentPlane<+1, 0> clipPosX; |
| cliputil::ComponentPlane<-1, 0> clipNegX; |
| cliputil::ComponentPlane<+1, 1> clipPosY; |
| cliputil::ComponentPlane<-1, 1> clipNegY; |
| cliputil::ComponentPlane<+1, 2> clipPosZ; |
| cliputil::ComponentPlane<-1, 2> clipNegZ; |
| |
| const std::vector<rr::VertexVaryingInfo> &fragInputs = |
| (program.geometryShader) ? (program.geometryShader->getOutputs()) : (program.vertexShader->getOutputs()); |
| const ClipVolumePlane *planes[] = {&clipPosX, &clipNegX, &clipPosY, &clipNegY, &clipPosZ, &clipNegZ}; |
| const int numPlanes = (clipWithZPlanes) ? (6) : (4); |
| |
| std::vector<pa::Triangle> outputTriangles; |
| |
| for (int inputTriangleNdx = 0; inputTriangleNdx < (int)list.size(); ++inputTriangleNdx) |
| { |
| bool clippedByPlane[6]; |
| |
| // Needs clipping? |
| { |
| bool discardPrimitive = false; |
| bool fullyInClipVolume = true; |
| |
| for (int planeNdx = 0; planeNdx < numPlanes; ++planeNdx) |
| { |
| const ClipVolumePlane *plane = planes[planeNdx]; |
| const bool v0InsidePlane = |
| plane->pointInClipVolume(vec4ToClipVec4(list[inputTriangleNdx].v0->position)); |
| const bool v1InsidePlane = |
| plane->pointInClipVolume(vec4ToClipVec4(list[inputTriangleNdx].v1->position)); |
| const bool v2InsidePlane = |
| plane->pointInClipVolume(vec4ToClipVec4(list[inputTriangleNdx].v2->position)); |
| |
| // Fully outside |
| if (!v0InsidePlane && !v1InsidePlane && !v2InsidePlane) |
| { |
| discardPrimitive = true; |
| break; |
| } |
| // Partially outside |
| else if (!v0InsidePlane || !v1InsidePlane || !v2InsidePlane) |
| { |
| clippedByPlane[planeNdx] = true; |
| fullyInClipVolume = false; |
| } |
| // Fully inside |
| else |
| clippedByPlane[planeNdx] = false; |
| } |
| |
| if (discardPrimitive) |
| continue; |
| |
| if (fullyInClipVolume) |
| { |
| outputTriangles.push_back(list[inputTriangleNdx]); |
| continue; |
| } |
| } |
| |
| // Clip |
| { |
| std::vector<SubTriangle> subTriangles(1); |
| SubTriangle &initialTri = subTriangles[0]; |
| |
| initialTri.vertices[0].position = vec4ToClipVec4(list[inputTriangleNdx].v0->position); |
| initialTri.vertices[0].weight[0] = (ClipFloat)1.0; |
| initialTri.vertices[0].weight[1] = (ClipFloat)0.0; |
| initialTri.vertices[0].weight[2] = (ClipFloat)0.0; |
| |
| initialTri.vertices[1].position = vec4ToClipVec4(list[inputTriangleNdx].v1->position); |
| initialTri.vertices[1].weight[0] = (ClipFloat)0.0; |
| initialTri.vertices[1].weight[1] = (ClipFloat)1.0; |
| initialTri.vertices[1].weight[2] = (ClipFloat)0.0; |
| |
| initialTri.vertices[2].position = vec4ToClipVec4(list[inputTriangleNdx].v2->position); |
| initialTri.vertices[2].weight[0] = (ClipFloat)0.0; |
| initialTri.vertices[2].weight[1] = (ClipFloat)0.0; |
| initialTri.vertices[2].weight[2] = (ClipFloat)1.0; |
| |
| // Clip all subtriangles to all relevant planes |
| for (int planeNdx = 0; planeNdx < numPlanes; ++planeNdx) |
| { |
| std::vector<SubTriangle> nextPhaseSubTriangles; |
| |
| if (!clippedByPlane[planeNdx]) |
| continue; |
| |
| for (int subTriangleNdx = 0; subTriangleNdx < (int)subTriangles.size(); ++subTriangleNdx) |
| { |
| std::vector<TriangleVertex> convexPrimitive; |
| |
| // Clip triangle and form a convex n-gon ( n c {3, 4} ) |
| clipTriangleToPlane(convexPrimitive, subTriangles[subTriangleNdx].vertices, *planes[planeNdx]); |
| |
| // Subtriangle completely discarded |
| if (convexPrimitive.empty()) |
| continue; |
| |
| DE_ASSERT(convexPrimitive.size() == 3 || convexPrimitive.size() == 4); |
| |
| //Triangulate planar convex n-gon |
| { |
| TriangleVertex &v0 = convexPrimitive[0]; |
| |
| for (int subsubTriangleNdx = 1; subsubTriangleNdx + 1 < (int)convexPrimitive.size(); |
| ++subsubTriangleNdx) |
| { |
| const float degenerateEpsilon = 1.0e-6f; |
| const TriangleVertex &v1 = convexPrimitive[subsubTriangleNdx]; |
| const TriangleVertex &v2 = convexPrimitive[subsubTriangleNdx + 1]; |
| const float visibleArea = de::abs(cross2D(to2DCartesian(clipVec4ToVec4(v1.position)) - |
| to2DCartesian(clipVec4ToVec4(v0.position)), |
| to2DCartesian(clipVec4ToVec4(v2.position)) - |
| to2DCartesian(clipVec4ToVec4(v0.position)))); |
| |
| // has surface area (is not a degenerate) |
| if (visibleArea >= degenerateEpsilon) |
| { |
| SubTriangle subsubTriangle; |
| |
| subsubTriangle.vertices[0] = v0; |
| subsubTriangle.vertices[1] = v1; |
| subsubTriangle.vertices[2] = v2; |
| |
| nextPhaseSubTriangles.push_back(subsubTriangle); |
| } |
| } |
| } |
| } |
| |
| subTriangles.swap(nextPhaseSubTriangles); |
| } |
| |
| // Rebuild pa::Triangles from subtriangles |
| for (int subTriangleNdx = 0; subTriangleNdx < (int)subTriangles.size(); ++subTriangleNdx) |
| { |
| VertexPacket *p0 = vpalloc.alloc(); |
| VertexPacket *p1 = vpalloc.alloc(); |
| VertexPacket *p2 = vpalloc.alloc(); |
| pa::Triangle ngonFragment(p0, p1, p2, -1); |
| |
| p0->position = clipVec4ToVec4(subTriangles[subTriangleNdx].vertices[0].position); |
| p1->position = clipVec4ToVec4(subTriangles[subTriangleNdx].vertices[1].position); |
| p2->position = clipVec4ToVec4(subTriangles[subTriangleNdx].vertices[2].position); |
| |
| for (size_t outputNdx = 0; outputNdx < fragInputs.size(); ++outputNdx) |
| { |
| if (fragInputs[outputNdx].type == GENERICVECTYPE_FLOAT) |
| { |
| const tcu::Vec4 out0 = list[inputTriangleNdx].v0->outputs[outputNdx].get<float>(); |
| const tcu::Vec4 out1 = list[inputTriangleNdx].v1->outputs[outputNdx].get<float>(); |
| const tcu::Vec4 out2 = list[inputTriangleNdx].v2->outputs[outputNdx].get<float>(); |
| |
| p0->outputs[outputNdx] = (float)subTriangles[subTriangleNdx].vertices[0].weight[0] * out0 + |
| (float)subTriangles[subTriangleNdx].vertices[0].weight[1] * out1 + |
| (float)subTriangles[subTriangleNdx].vertices[0].weight[2] * out2; |
| |
| p1->outputs[outputNdx] = (float)subTriangles[subTriangleNdx].vertices[1].weight[0] * out0 + |
| (float)subTriangles[subTriangleNdx].vertices[1].weight[1] * out1 + |
| (float)subTriangles[subTriangleNdx].vertices[1].weight[2] * out2; |
| |
| p2->outputs[outputNdx] = (float)subTriangles[subTriangleNdx].vertices[2].weight[0] * out0 + |
| (float)subTriangles[subTriangleNdx].vertices[2].weight[1] * out1 + |
| (float)subTriangles[subTriangleNdx].vertices[2].weight[2] * out2; |
| } |
| else |
| { |
| // only floats are interpolated, all others must be flatshaded then |
| p0->outputs[outputNdx] = list[inputTriangleNdx].getProvokingVertex()->outputs[outputNdx]; |
| p1->outputs[outputNdx] = list[inputTriangleNdx].getProvokingVertex()->outputs[outputNdx]; |
| p2->outputs[outputNdx] = list[inputTriangleNdx].getProvokingVertex()->outputs[outputNdx]; |
| } |
| } |
| |
| outputTriangles.push_back(ngonFragment); |
| } |
| } |
| } |
| |
| // output result |
| list.swap(outputTriangles); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * Clip lines to the near and far clip planes. |
| * |
| * Clipping to other planes is a by-product of the viewport test (i.e. |
| * rasterization area selection). |
| *//*--------------------------------------------------------------------*/ |
| void clipPrimitives(std::vector<pa::Line> &list, const Program &program, bool clipWithZPlanes, |
| VertexPacketAllocator &vpalloc) |
| { |
| DE_UNREF(vpalloc); |
| |
| using namespace cliputil; |
| |
| // Lines are clipped only by the far and the near planes here. Line clipping by other planes done in the rasterization phase |
| |
| const std::vector<rr::VertexVaryingInfo> &fragInputs = |
| (program.geometryShader) ? (program.geometryShader->getOutputs()) : (program.vertexShader->getOutputs()); |
| std::vector<pa::Line> visibleLines; |
| |
| // Z-clipping disabled, don't do anything |
| if (!clipWithZPlanes) |
| return; |
| |
| for (size_t ndx = 0; ndx < list.size(); ++ndx) |
| { |
| pa::Line &l = list[ndx]; |
| |
| // Totally discarded? |
| if ((l.v0->position.z() < -l.v0->position.w() && l.v1->position.z() < -l.v1->position.w()) || |
| (l.v0->position.z() > l.v0->position.w() && l.v1->position.z() > l.v1->position.w())) |
| continue; // discard |
| |
| // Something is visible |
| |
| const ClipVec4 p0 = vec4ToClipVec4(l.v0->position); |
| const ClipVec4 p1 = vec4ToClipVec4(l.v1->position); |
| const ClipFloat t0 = getLineEndpointClipping(p0, p1); |
| const ClipFloat t1 = getLineEndpointClipping(p1, p0); |
| |
| // Not clipped at all? |
| if (t0 == (ClipFloat)0.0 && t1 == (ClipFloat)0.0) |
| { |
| visibleLines.push_back(pa::Line(l.v0, l.v1, -1)); |
| } |
| else |
| { |
| // Clip position |
| l.v0->position = clipVec4ToVec4(tcu::mix(p0, p1, t0)); |
| l.v1->position = clipVec4ToVec4(tcu::mix(p1, p0, t1)); |
| |
| // Clip attributes |
| for (size_t outputNdx = 0; outputNdx < fragInputs.size(); ++outputNdx) |
| { |
| // only floats are clipped, other types are flatshaded |
| if (fragInputs[outputNdx].type == GENERICVECTYPE_FLOAT) |
| { |
| const tcu::Vec4 a0 = l.v0->outputs[outputNdx].get<float>(); |
| const tcu::Vec4 a1 = l.v1->outputs[outputNdx].get<float>(); |
| |
| l.v0->outputs[outputNdx] = tcu::mix(a0, a1, (float)t0); |
| l.v1->outputs[outputNdx] = tcu::mix(a1, a0, (float)t1); |
| } |
| } |
| |
| visibleLines.push_back(pa::Line(l.v0, l.v1, -1)); |
| } |
| } |
| |
| // return visible in list |
| std::swap(visibleLines, list); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * Discard points not within clip volume. Clipping is a by-product |
| * of the viewport test. |
| *//*--------------------------------------------------------------------*/ |
| void clipPrimitives(std::vector<pa::Point> &list, const Program &program, bool clipWithZPlanes, |
| VertexPacketAllocator &vpalloc) |
| { |
| DE_UNREF(vpalloc); |
| DE_UNREF(program); |
| |
| std::vector<pa::Point> visiblePoints; |
| |
| // Z-clipping disabled, don't do anything |
| if (!clipWithZPlanes) |
| return; |
| |
| for (size_t ndx = 0; ndx < list.size(); ++ndx) |
| { |
| pa::Point &p = list[ndx]; |
| |
| // points are discarded if Z is not in range. (Wide) point clipping is done in the rasterization phase |
| if (de::inRange(p.v0->position.z(), -p.v0->position.w(), p.v0->position.w())) |
| visiblePoints.push_back(pa::Point(p.v0)); |
| } |
| |
| // return visible in list |
| std::swap(visiblePoints, list); |
| } |
| |
| void transformVertexClipCoordsToWindowCoords(const RenderState &state, VertexPacket &packet) |
| { |
| // To normalized device coords |
| { |
| packet.position = |
| tcu::Vec4(packet.position.x() / packet.position.w(), packet.position.y() / packet.position.w(), |
| packet.position.z() / packet.position.w(), 1.0f / packet.position.w()); |
| } |
| |
| // To window coords |
| { |
| const WindowRectangle &viewport = state.viewport.rect; |
| const float halfW = (float)(viewport.width) / 2.0f; |
| const float halfH = (float)(viewport.height) / 2.0f; |
| const float oX = (float)viewport.left + halfW; |
| const float oY = (float)viewport.bottom + halfH; |
| const float zn = state.viewport.zn; |
| const float zf = state.viewport.zf; |
| |
| packet.position = tcu::Vec4(packet.position.x() * halfW + oX, packet.position.y() * halfH + oY, |
| packet.position.z() * (zf - zn) / 2.0f + (zn + zf) / 2.0f, packet.position.w()); |
| } |
| } |
| |
| void transformPrimitiveClipCoordsToWindowCoords(const RenderState &state, pa::Triangle &target) |
| { |
| transformVertexClipCoordsToWindowCoords(state, *target.v0); |
| transformVertexClipCoordsToWindowCoords(state, *target.v1); |
| transformVertexClipCoordsToWindowCoords(state, *target.v2); |
| } |
| |
| void transformPrimitiveClipCoordsToWindowCoords(const RenderState &state, pa::Line &target) |
| { |
| transformVertexClipCoordsToWindowCoords(state, *target.v0); |
| transformVertexClipCoordsToWindowCoords(state, *target.v1); |
| } |
| |
| void transformPrimitiveClipCoordsToWindowCoords(const RenderState &state, pa::Point &target) |
| { |
| transformVertexClipCoordsToWindowCoords(state, *target.v0); |
| } |
| |
| template <typename ContainerType> |
| void transformClipCoordsToWindowCoords(const RenderState &state, ContainerType &list) |
| { |
| for (typename ContainerType::iterator it = list.begin(); it != list.end(); ++it) |
| transformPrimitiveClipCoordsToWindowCoords(state, *it); |
| } |
| |
| void makeSharedVerticeDistinct(VertexPacket *&packet, std::set<VertexPacket *, std::less<void *>> &vertices, |
| VertexPacketAllocator &vpalloc) |
| { |
| // distinct |
| if (vertices.find(packet) == vertices.end()) |
| { |
| vertices.insert(packet); |
| } |
| else |
| { |
| VertexPacket *newPacket = vpalloc.alloc(); |
| |
| // copy packet output values |
| newPacket->position = packet->position; |
| newPacket->pointSize = packet->pointSize; |
| newPacket->primitiveID = packet->primitiveID; |
| |
| for (size_t outputNdx = 0; outputNdx < vpalloc.getNumVertexOutputs(); ++outputNdx) |
| newPacket->outputs[outputNdx] = packet->outputs[outputNdx]; |
| |
| // no need to insert new packet to "vertices" as newPacket is unique |
| packet = newPacket; |
| } |
| } |
| |
| void makeSharedVerticesDistinct(pa::Triangle &target, std::set<VertexPacket *, std::less<void *>> &vertices, |
| VertexPacketAllocator &vpalloc) |
| { |
| makeSharedVerticeDistinct(target.v0, vertices, vpalloc); |
| makeSharedVerticeDistinct(target.v1, vertices, vpalloc); |
| makeSharedVerticeDistinct(target.v2, vertices, vpalloc); |
| } |
| |
| void makeSharedVerticesDistinct(pa::Line &target, std::set<VertexPacket *, std::less<void *>> &vertices, |
| VertexPacketAllocator &vpalloc) |
| { |
| makeSharedVerticeDistinct(target.v0, vertices, vpalloc); |
| makeSharedVerticeDistinct(target.v1, vertices, vpalloc); |
| } |
| |
| void makeSharedVerticesDistinct(pa::Point &target, std::set<VertexPacket *, std::less<void *>> &vertices, |
| VertexPacketAllocator &vpalloc) |
| { |
| makeSharedVerticeDistinct(target.v0, vertices, vpalloc); |
| } |
| |
| template <typename ContainerType> |
| void makeSharedVerticesDistinct(ContainerType &list, VertexPacketAllocator &vpalloc) |
| { |
| std::set<VertexPacket *, std::less<void *>> vertices; |
| |
| for (typename ContainerType::iterator it = list.begin(); it != list.end(); ++it) |
| makeSharedVerticesDistinct(*it, vertices, vpalloc); |
| } |
| |
| void generatePrimitiveIDs(pa::Triangle &target, int id) |
| { |
| target.v0->primitiveID = id; |
| target.v1->primitiveID = id; |
| target.v2->primitiveID = id; |
| } |
| |
| void generatePrimitiveIDs(pa::Line &target, int id) |
| { |
| target.v0->primitiveID = id; |
| target.v1->primitiveID = id; |
| } |
| |
| void generatePrimitiveIDs(pa::Point &target, int id) |
| { |
| target.v0->primitiveID = id; |
| } |
| |
| template <typename ContainerType> |
| void generatePrimitiveIDs(ContainerType &list, DrawContext &drawContext) |
| { |
| for (typename ContainerType::iterator it = list.begin(); it != list.end(); ++it) |
| generatePrimitiveIDs(*it, drawContext.primitiveID++); |
| } |
| |
| static float findTriangleVertexDepthSlope(const tcu::Vec4 &p, const tcu::Vec4 &v0, const tcu::Vec4 &v1) |
| { |
| // screen space |
| const tcu::Vec3 ssp = p.swizzle(0, 1, 2); |
| const tcu::Vec3 ssv0 = v0.swizzle(0, 1, 2); |
| const tcu::Vec3 ssv1 = v1.swizzle(0, 1, 2); |
| |
| // dx & dy |
| |
| const tcu::Vec3 a = ssv0.swizzle(0, 1, 2) - ssp.swizzle(0, 1, 2); |
| const tcu::Vec3 b = ssv1.swizzle(0, 1, 2) - ssp.swizzle(0, 1, 2); |
| const float epsilon = 0.0001f; |
| const float det = (a.x() * b.y() - b.x() * a.y()); |
| |
| // degenerate triangle, it won't generate any fragments anyway. Return value doesn't matter |
| if (de::abs(det) < epsilon) |
| return 0.0f; |
| |
| const tcu::Vec2 dxDir = tcu::Vec2(b.y(), -a.y()) / det; |
| const tcu::Vec2 dyDir = tcu::Vec2(-b.x(), a.x()) / det; |
| |
| const float dzdx = dxDir.x() * a.z() + dxDir.y() * b.z(); |
| const float dzdy = dyDir.x() * a.z() + dyDir.y() * b.z(); |
| |
| // approximate using max(|dz/dx|, |dz/dy|) |
| return de::max(de::abs(dzdx), de::abs(dzdy)); |
| } |
| |
| static float findPrimitiveMaximumDepthSlope(const pa::Triangle &triangle) |
| { |
| const float d1 = findTriangleVertexDepthSlope(triangle.v0->position, triangle.v1->position, triangle.v2->position); |
| const float d2 = findTriangleVertexDepthSlope(triangle.v1->position, triangle.v2->position, triangle.v0->position); |
| const float d3 = findTriangleVertexDepthSlope(triangle.v2->position, triangle.v0->position, triangle.v1->position); |
| |
| return de::max(d1, de::max(d2, d3)); |
| } |
| |
| static float getFloatingPointMinimumResolvableDifference(float maxZValue, tcu::TextureFormat::ChannelType type) |
| { |
| if (type == tcu::TextureFormat::FLOAT) |
| { |
| // 32f |
| const int maxExponent = tcu::Float32(maxZValue).exponent(); |
| return tcu::Float32::construct(+1, maxExponent - 23, 1 << 23).asFloat(); |
| } |
| |
| // unexpected format |
| DE_ASSERT(false); |
| return 0.0f; |
| } |
| |
| static float getFixedPointMinimumResolvableDifference(int numBits) |
| { |
| return tcu::Float32::construct(+1, -numBits, 1 << 23).asFloat(); |
| } |
| |
| static float findPrimitiveMinimumResolvableDifference(const pa::Triangle &triangle, |
| const rr::MultisampleConstPixelBufferAccess &depthAccess) |
| { |
| const float maxZvalue = |
| de::max(de::max(triangle.v0->position.z(), triangle.v1->position.z()), triangle.v2->position.z()); |
| const tcu::TextureFormat format = depthAccess.raw().getFormat(); |
| const tcu::TextureFormat::ChannelOrder order = format.order; |
| |
| if (order == tcu::TextureFormat::D) |
| { |
| // depth only |
| const tcu::TextureFormat::ChannelType channelType = format.type; |
| const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(channelType); |
| const int numBits = tcu::getTextureFormatBitDepth(format).x(); |
| |
| if (channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT) |
| return getFloatingPointMinimumResolvableDifference(maxZvalue, channelType); |
| else |
| // \note channelClass might be CLASS_LAST but that's ok |
| return getFixedPointMinimumResolvableDifference(numBits); |
| } |
| else if (order == tcu::TextureFormat::DS) |
| { |
| // depth stencil, special cases for possible combined formats |
| if (format.type == tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV) |
| return getFloatingPointMinimumResolvableDifference(maxZvalue, tcu::TextureFormat::FLOAT); |
| else if (format.type == tcu::TextureFormat::UNSIGNED_INT_24_8) |
| return getFixedPointMinimumResolvableDifference(24); |
| } |
| |
| // unexpected format |
| DE_ASSERT(false); |
| return 0.0f; |
| } |
| |
| void writeFragmentPackets(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| const FragmentPacket *fragmentPackets, int numRasterizedPackets, rr::FaceType facetype, |
| const std::vector<rr::GenericVec4> &fragmentOutputArray, |
| const std::vector<rr::GenericVec4> &fragmentOutputArraySrc1, const float *depthValues, |
| std::vector<Fragment> &fragmentBuffer) |
| { |
| const int numSamples = renderTarget.getNumSamples(); |
| const size_t numOutputs = program.fragmentShader->getOutputs().size(); |
| FragmentProcessor fragProcessor; |
| |
| DE_ASSERT(fragmentOutputArray.size() >= (size_t)numRasterizedPackets * 4 * numOutputs); |
| DE_ASSERT(fragmentBuffer.size() >= (size_t)numRasterizedPackets * 4); |
| |
| // Translate fragments but do not set the value yet |
| { |
| int fragCount = 0; |
| for (int packetNdx = 0; packetNdx < numRasterizedPackets; ++packetNdx) |
| for (int fragNdx = 0; fragNdx < 4; fragNdx++) |
| { |
| const FragmentPacket &packet = fragmentPackets[packetNdx]; |
| const int xo = fragNdx % 2; |
| const int yo = fragNdx / 2; |
| |
| if (getCoverageAnyFragmentSampleLive(packet.coverage, numSamples, xo, yo)) |
| { |
| Fragment &fragment = fragmentBuffer[fragCount++]; |
| |
| fragment.pixelCoord = packet.position + tcu::IVec2(xo, yo); |
| fragment.coverage = |
| (uint32_t)((packet.coverage & getCoverageFragmentSampleBits(numSamples, xo, yo)) >> |
| getCoverageOffset(numSamples, xo, yo)); |
| fragment.sampleDepths = |
| (depthValues) ? (&depthValues[(packetNdx * 4 + yo * 2 + xo) * numSamples]) : (DE_NULL); |
| } |
| } |
| } |
| |
| // Set per output output values |
| { |
| rr::FragmentOperationState noStencilDepthWriteState(state.fragOps); |
| noStencilDepthWriteState.depthMask = false; |
| noStencilDepthWriteState.stencilStates[facetype].sFail = STENCILOP_KEEP; |
| noStencilDepthWriteState.stencilStates[facetype].dpFail = STENCILOP_KEEP; |
| noStencilDepthWriteState.stencilStates[facetype].dpPass = STENCILOP_KEEP; |
| |
| int fragCount = 0; |
| for (size_t outputNdx = 0; outputNdx < numOutputs; ++outputNdx) |
| { |
| // Only the last output-pass has default state, other passes have stencil & depth writemask=0 |
| const rr::FragmentOperationState &fragOpsState = |
| (outputNdx == numOutputs - 1) ? (state.fragOps) : (noStencilDepthWriteState); |
| |
| for (int packetNdx = 0; packetNdx < numRasterizedPackets; ++packetNdx) |
| for (int fragNdx = 0; fragNdx < 4; fragNdx++) |
| { |
| const FragmentPacket &packet = fragmentPackets[packetNdx]; |
| const int xo = fragNdx % 2; |
| const int yo = fragNdx / 2; |
| |
| // Add only fragments that have live samples to shaded fragments queue. |
| if (getCoverageAnyFragmentSampleLive(packet.coverage, numSamples, xo, yo)) |
| { |
| Fragment &fragment = fragmentBuffer[fragCount++]; |
| fragment.value = fragmentOutputArray[(packetNdx * 4 + fragNdx) * numOutputs + outputNdx]; |
| fragment.value1 = fragmentOutputArraySrc1[(packetNdx * 4 + fragNdx) * numOutputs + outputNdx]; |
| } |
| } |
| |
| // Execute per-fragment ops and write |
| fragProcessor.render(renderTarget.getColorBuffer((int)outputNdx), renderTarget.getDepthBuffer(), |
| renderTarget.getStencilBuffer(), &fragmentBuffer[0], fragCount, facetype, |
| fragOpsState); |
| } |
| } |
| } |
| |
| void rasterizePrimitive(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| const pa::Triangle &triangle, const tcu::IVec4 &renderTargetRect, |
| RasterizationInternalBuffers &buffers) |
| { |
| const int numSamples = renderTarget.getNumSamples(); |
| const float depthClampMin = de::min(state.viewport.zn, state.viewport.zf); |
| const float depthClampMax = de::max(state.viewport.zn, state.viewport.zf); |
| TriangleRasterizer rasterizer(renderTargetRect, numSamples, state.rasterization, state.subpixelBits); |
| float depthOffset = 0.0f; |
| |
| rasterizer.init(triangle.v0->position, triangle.v1->position, triangle.v2->position); |
| |
| // Culling |
| const FaceType visibleFace = rasterizer.getVisibleFace(); |
| if ((state.cullMode == CULLMODE_FRONT && visibleFace == FACETYPE_FRONT) || |
| (state.cullMode == CULLMODE_BACK && visibleFace == FACETYPE_BACK)) |
| return; |
| |
| // Shading context |
| FragmentShadingContext shadingContext( |
| triangle.v0->outputs, triangle.v1->outputs, triangle.v2->outputs, &buffers.shaderOutputs[0], |
| &buffers.shaderOutputsSrc1[0], buffers.fragmentDepthBuffer, triangle.v2->primitiveID, |
| (int)program.fragmentShader->getOutputs().size(), numSamples, rasterizer.getVisibleFace()); |
| |
| // Polygon offset |
| if (buffers.fragmentDepthBuffer && state.fragOps.polygonOffsetEnabled) |
| { |
| const float maximumDepthSlope = findPrimitiveMaximumDepthSlope(triangle); |
| const float minimumResolvableDifference = |
| findPrimitiveMinimumResolvableDifference(triangle, renderTarget.getDepthBuffer()); |
| |
| depthOffset = maximumDepthSlope * state.fragOps.polygonOffsetFactor + |
| minimumResolvableDifference * state.fragOps.polygonOffsetUnits; |
| } |
| |
| // Execute rasterize - shade - write loop |
| for (;;) |
| { |
| const int maxFragmentPackets = (int)buffers.fragmentPackets.size(); |
| int numRasterizedPackets = 0; |
| |
| // Rasterize |
| |
| rasterizer.rasterize(&buffers.fragmentPackets[0], buffers.fragmentDepthBuffer, maxFragmentPackets, |
| numRasterizedPackets); |
| |
| // numRasterizedPackets is guaranteed to be greater than zero for shadeFragments() |
| |
| if (!numRasterizedPackets) |
| break; // Rasterization finished. |
| |
| // Polygon offset |
| if (buffers.fragmentDepthBuffer && state.fragOps.polygonOffsetEnabled) |
| for (int sampleNdx = 0; sampleNdx < numRasterizedPackets * 4 * numSamples; ++sampleNdx) |
| buffers.fragmentDepthBuffer[sampleNdx] = |
| de::clamp(buffers.fragmentDepthBuffer[sampleNdx] + depthOffset, 0.0f, 1.0f); |
| |
| // Shade |
| |
| program.fragmentShader->shadeFragments(&buffers.fragmentPackets[0], numRasterizedPackets, shadingContext); |
| |
| // Depth clamp |
| if (buffers.fragmentDepthBuffer && state.fragOps.depthClampEnabled) |
| for (int sampleNdx = 0; sampleNdx < numRasterizedPackets * 4 * numSamples; ++sampleNdx) |
| buffers.fragmentDepthBuffer[sampleNdx] = |
| de::clamp(buffers.fragmentDepthBuffer[sampleNdx], depthClampMin, depthClampMax); |
| |
| // Handle fragment shader outputs |
| |
| writeFragmentPackets(state, renderTarget, program, &buffers.fragmentPackets[0], numRasterizedPackets, |
| visibleFace, buffers.shaderOutputs, buffers.shaderOutputsSrc1, buffers.fragmentDepthBuffer, |
| buffers.shadedFragments); |
| } |
| } |
| |
| void rasterizePrimitive(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| const pa::Line &line, const tcu::IVec4 &renderTargetRect, RasterizationInternalBuffers &buffers) |
| { |
| const int numSamples = renderTarget.getNumSamples(); |
| const float depthClampMin = de::min(state.viewport.zn, state.viewport.zf); |
| const float depthClampMax = de::max(state.viewport.zn, state.viewport.zf); |
| const bool msaa = numSamples > 1; |
| FragmentShadingContext shadingContext(line.v0->outputs, line.v1->outputs, DE_NULL, &buffers.shaderOutputs[0], |
| &buffers.shaderOutputsSrc1[0], buffers.fragmentDepthBuffer, |
| line.v1->primitiveID, (int)program.fragmentShader->getOutputs().size(), |
| numSamples, FACETYPE_FRONT); |
| SingleSampleLineRasterizer aliasedRasterizer(renderTargetRect, state.subpixelBits); |
| MultiSampleLineRasterizer msaaRasterizer(numSamples, renderTargetRect, state.subpixelBits); |
| |
| // Initialize rasterization. |
| if (msaa) |
| msaaRasterizer.init(line.v0->position, line.v1->position, state.line.lineWidth); |
| else |
| aliasedRasterizer.init(line.v0->position, line.v1->position, state.line.lineWidth, 1, 0xFFFF); |
| |
| for (;;) |
| { |
| const int maxFragmentPackets = (int)buffers.fragmentPackets.size(); |
| int numRasterizedPackets = 0; |
| |
| // Rasterize |
| |
| if (msaa) |
| msaaRasterizer.rasterize(&buffers.fragmentPackets[0], buffers.fragmentDepthBuffer, maxFragmentPackets, |
| numRasterizedPackets); |
| else |
| aliasedRasterizer.rasterize(&buffers.fragmentPackets[0], buffers.fragmentDepthBuffer, maxFragmentPackets, |
| numRasterizedPackets); |
| |
| // numRasterizedPackets is guaranteed to be greater than zero for shadeFragments() |
| |
| if (!numRasterizedPackets) |
| break; // Rasterization finished. |
| |
| // Shade |
| |
| program.fragmentShader->shadeFragments(&buffers.fragmentPackets[0], numRasterizedPackets, shadingContext); |
| |
| // Depth clamp |
| if (buffers.fragmentDepthBuffer && state.fragOps.depthClampEnabled) |
| for (int sampleNdx = 0; sampleNdx < numRasterizedPackets * 4 * numSamples; ++sampleNdx) |
| buffers.fragmentDepthBuffer[sampleNdx] = |
| de::clamp(buffers.fragmentDepthBuffer[sampleNdx], depthClampMin, depthClampMax); |
| |
| // Handle fragment shader outputs |
| |
| writeFragmentPackets(state, renderTarget, program, &buffers.fragmentPackets[0], numRasterizedPackets, |
| rr::FACETYPE_FRONT, buffers.shaderOutputs, buffers.shaderOutputsSrc1, |
| buffers.fragmentDepthBuffer, buffers.shadedFragments); |
| } |
| } |
| |
| void rasterizePrimitive(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| const pa::Point &point, const tcu::IVec4 &renderTargetRect, |
| RasterizationInternalBuffers &buffers) |
| { |
| const int numSamples = renderTarget.getNumSamples(); |
| const float depthClampMin = de::min(state.viewport.zn, state.viewport.zf); |
| const float depthClampMax = de::max(state.viewport.zn, state.viewport.zf); |
| TriangleRasterizer rasterizer1(renderTargetRect, numSamples, state.rasterization, state.subpixelBits); |
| TriangleRasterizer rasterizer2(renderTargetRect, numSamples, state.rasterization, state.subpixelBits); |
| |
| // draw point as two triangles |
| const float offset = point.v0->pointSize / 2.0f; |
| const tcu::Vec4 w0 = tcu::Vec4(point.v0->position.x() + offset, point.v0->position.y() + offset, |
| point.v0->position.z(), point.v0->position.w()); |
| const tcu::Vec4 w1 = tcu::Vec4(point.v0->position.x() - offset, point.v0->position.y() + offset, |
| point.v0->position.z(), point.v0->position.w()); |
| const tcu::Vec4 w2 = tcu::Vec4(point.v0->position.x() - offset, point.v0->position.y() - offset, |
| point.v0->position.z(), point.v0->position.w()); |
| const tcu::Vec4 w3 = tcu::Vec4(point.v0->position.x() + offset, point.v0->position.y() - offset, |
| point.v0->position.z(), point.v0->position.w()); |
| |
| rasterizer1.init(w0, w1, w2); |
| rasterizer2.init(w0, w2, w3); |
| |
| // Shading context |
| FragmentShadingContext shadingContext(point.v0->outputs, DE_NULL, DE_NULL, &buffers.shaderOutputs[0], |
| &buffers.shaderOutputsSrc1[0], buffers.fragmentDepthBuffer, |
| point.v0->primitiveID, (int)program.fragmentShader->getOutputs().size(), |
| numSamples, FACETYPE_FRONT); |
| |
| // Execute rasterize - shade - write loop |
| for (;;) |
| { |
| const int maxFragmentPackets = (int)buffers.fragmentPackets.size(); |
| int numRasterizedPackets = 0; |
| |
| // Rasterize both triangles |
| |
| rasterizer1.rasterize(&buffers.fragmentPackets[0], buffers.fragmentDepthBuffer, maxFragmentPackets, |
| numRasterizedPackets); |
| if (numRasterizedPackets != maxFragmentPackets) |
| { |
| float *const depthBufferAppendPointer = |
| (buffers.fragmentDepthBuffer) ? (buffers.fragmentDepthBuffer + numRasterizedPackets * numSamples * 4) : |
| (DE_NULL); |
| int numRasterizedPackets2 = 0; |
| |
| rasterizer2.rasterize(&buffers.fragmentPackets[numRasterizedPackets], depthBufferAppendPointer, |
| maxFragmentPackets - numRasterizedPackets, numRasterizedPackets2); |
| |
| numRasterizedPackets += numRasterizedPackets2; |
| } |
| |
| // numRasterizedPackets is guaranteed to be greater than zero for shadeFragments() |
| |
| if (!numRasterizedPackets) |
| break; // Rasterization finished. |
| |
| // Shade |
| |
| program.fragmentShader->shadeFragments(&buffers.fragmentPackets[0], numRasterizedPackets, shadingContext); |
| |
| // Depth clamp |
| if (buffers.fragmentDepthBuffer && state.fragOps.depthClampEnabled) |
| for (int sampleNdx = 0; sampleNdx < numRasterizedPackets * 4 * numSamples; ++sampleNdx) |
| buffers.fragmentDepthBuffer[sampleNdx] = |
| de::clamp(buffers.fragmentDepthBuffer[sampleNdx], depthClampMin, depthClampMax); |
| |
| // Handle fragment shader outputs |
| |
| writeFragmentPackets(state, renderTarget, program, &buffers.fragmentPackets[0], numRasterizedPackets, |
| rr::FACETYPE_FRONT, buffers.shaderOutputs, buffers.shaderOutputsSrc1, |
| buffers.fragmentDepthBuffer, buffers.shadedFragments); |
| } |
| } |
| |
| template <typename ContainerType> |
| void rasterize(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| const ContainerType &list) |
| { |
| const int numSamples = renderTarget.getNumSamples(); |
| const int numFragmentOutputs = (int)program.fragmentShader->getOutputs().size(); |
| const size_t maxFragmentPackets = 128; |
| |
| const tcu::IVec4 viewportRect = tcu::IVec4(state.viewport.rect.left, state.viewport.rect.bottom, |
| state.viewport.rect.width, state.viewport.rect.height); |
| const tcu::IVec4 bufferRect = getBufferSize(renderTarget.getColorBuffer(0)); |
| const tcu::IVec4 renderTargetRect = rectIntersection(viewportRect, bufferRect); |
| |
| // shared buffers for all primitives |
| std::vector<FragmentPacket> fragmentPackets(maxFragmentPackets); |
| std::vector<GenericVec4> shaderOutputs(maxFragmentPackets * 4 * numFragmentOutputs); |
| std::vector<GenericVec4> shaderOutputsSrc1(maxFragmentPackets * 4 * numFragmentOutputs); |
| std::vector<Fragment> shadedFragments(maxFragmentPackets * 4); |
| std::vector<float> depthValues(0); |
| float *depthBufferPointer = DE_NULL; |
| |
| RasterizationInternalBuffers buffers; |
| |
| // calculate depth only if we have a depth buffer |
| if (!isEmpty(renderTarget.getDepthBuffer())) |
| { |
| depthValues.resize(maxFragmentPackets * 4 * numSamples); |
| depthBufferPointer = &depthValues[0]; |
| } |
| |
| // set buffers |
| buffers.fragmentPackets.swap(fragmentPackets); |
| buffers.shaderOutputs.swap(shaderOutputs); |
| buffers.shaderOutputsSrc1.swap(shaderOutputsSrc1); |
| buffers.shadedFragments.swap(shadedFragments); |
| buffers.fragmentDepthBuffer = depthBufferPointer; |
| |
| // rasterize |
| for (typename ContainerType::const_iterator it = list.begin(); it != list.end(); ++it) |
| rasterizePrimitive(state, renderTarget, program, *it, renderTargetRect, buffers); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * Draws transformed triangles, lines or points to render target |
| *//*--------------------------------------------------------------------*/ |
| template <typename ContainerType> |
| void drawBasicPrimitives(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| ContainerType &primList, VertexPacketAllocator &vpalloc) |
| { |
| const bool clipZ = !state.fragOps.depthClampEnabled; |
| |
| // Transform feedback |
| |
| // Flatshading |
| flatshadeVertices(program, primList); |
| |
| // Clipping |
| // \todo [jarkko] is creating & swapping std::vectors really a good solution? |
| clipPrimitives(primList, program, clipZ, vpalloc); |
| |
| // Transform vertices to window coords |
| transformClipCoordsToWindowCoords(state, primList); |
| |
| // Rasterize and paint |
| rasterize(state, renderTarget, program, primList); |
| } |
| |
| void copyVertexPacketPointers(const VertexPacket **dst, const pa::Point &in) |
| { |
| dst[0] = in.v0; |
| } |
| |
| void copyVertexPacketPointers(const VertexPacket **dst, const pa::Line &in) |
| { |
| dst[0] = in.v0; |
| dst[1] = in.v1; |
| } |
| |
| void copyVertexPacketPointers(const VertexPacket **dst, const pa::Triangle &in) |
| { |
| dst[0] = in.v0; |
| dst[1] = in.v1; |
| dst[2] = in.v2; |
| } |
| |
| void copyVertexPacketPointers(const VertexPacket **dst, const pa::LineAdjacency &in) |
| { |
| dst[0] = in.v0; |
| dst[1] = in.v1; |
| dst[2] = in.v2; |
| dst[3] = in.v3; |
| } |
| |
| void copyVertexPacketPointers(const VertexPacket **dst, const pa::TriangleAdjacency &in) |
| { |
| dst[0] = in.v0; |
| dst[1] = in.v1; |
| dst[2] = in.v2; |
| dst[3] = in.v3; |
| dst[4] = in.v4; |
| dst[5] = in.v5; |
| } |
| |
| template <PrimitiveType DrawPrimitiveType> // \note DrawPrimitiveType can only be Points, line_strip, or triangle_strip |
| void drawGeometryShaderOutputAsPrimitives(const RenderState &state, const RenderTarget &renderTarget, |
| const Program &program, VertexPacket *const *vertices, size_t numVertices, |
| VertexPacketAllocator &vpalloc) |
| { |
| // Run primitive assembly for generated stream |
| |
| const size_t assemblerPrimitiveCount = |
| PrimitiveTypeTraits<DrawPrimitiveType>::Assembler::getPrimitiveCount(numVertices); |
| std::vector<typename PrimitiveTypeTraits<DrawPrimitiveType>::BaseType> inputPrimitives(assemblerPrimitiveCount); |
| |
| PrimitiveTypeTraits<DrawPrimitiveType>::Assembler::exec( |
| inputPrimitives.begin(), vertices, numVertices, |
| state |
| .provokingVertexConvention); // \note input Primitives are baseType_t => only basic primitives (non adjacency) will compile |
| |
| // Make shared vertices distinct |
| |
| makeSharedVerticesDistinct(inputPrimitives, vpalloc); |
| |
| // Draw assembled primitives |
| |
| drawBasicPrimitives(state, renderTarget, program, inputPrimitives, vpalloc); |
| } |
| |
| template <PrimitiveType DrawPrimitiveType> |
| void drawWithGeometryShader(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| std::vector<typename PrimitiveTypeTraits<DrawPrimitiveType>::Type> &input, |
| DrawContext &drawContext) |
| { |
| // Vertices outputted by geometry shader may have different number of output variables than the original, create new memory allocator |
| VertexPacketAllocator vpalloc(program.geometryShader->getOutputs().size()); |
| |
| // Run geometry shader for all primitives |
| GeometryEmitter emitter(vpalloc, program.geometryShader->getNumVerticesOut()); |
| std::vector<PrimitivePacket> primitives(input.size()); |
| const int numInvocations = (int)program.geometryShader->getNumInvocations(); |
| const int verticesIn = PrimitiveTypeTraits<DrawPrimitiveType>::Type::NUM_VERTICES; |
| |
| for (size_t primitiveNdx = 0; primitiveNdx < input.size(); ++primitiveNdx) |
| { |
| primitives[primitiveNdx].primitiveIDIn = drawContext.primitiveID++; |
| copyVertexPacketPointers(primitives[primitiveNdx].vertices, input[primitiveNdx]); |
| } |
| |
| if (primitives.empty()) |
| return; |
| |
| for (int invocationNdx = 0; invocationNdx < numInvocations; ++invocationNdx) |
| { |
| // Shading invocation |
| |
| program.geometryShader->shadePrimitives(emitter, verticesIn, &primitives[0], (int)primitives.size(), |
| invocationNdx); |
| |
| // Find primitives in the emitted vertices |
| |
| std::vector<VertexPacket *> emitted; |
| emitter.moveEmittedTo(emitted); |
| |
| for (size_t primitiveBegin = 0; primitiveBegin < emitted.size();) |
| { |
| size_t primitiveEnd; |
| |
| // Find primitive begin |
| if (!emitted[primitiveBegin]) |
| { |
| ++primitiveBegin; |
| continue; |
| } |
| |
| // Find primitive end |
| |
| primitiveEnd = primitiveBegin + 1; |
| for (; (primitiveEnd < emitted.size()) && emitted[primitiveEnd]; ++primitiveEnd) |
| ; // find primitive end |
| |
| // Draw range [begin, end) |
| |
| switch (program.geometryShader->getOutputType()) |
| { |
| case rr::GEOMETRYSHADEROUTPUTTYPE_POINTS: |
| drawGeometryShaderOutputAsPrimitives<PRIMITIVETYPE_POINTS>( |
| state, renderTarget, program, &emitted[primitiveBegin], primitiveEnd - primitiveBegin, vpalloc); |
| break; |
| case rr::GEOMETRYSHADEROUTPUTTYPE_LINE_STRIP: |
| drawGeometryShaderOutputAsPrimitives<PRIMITIVETYPE_LINE_STRIP>( |
| state, renderTarget, program, &emitted[primitiveBegin], primitiveEnd - primitiveBegin, vpalloc); |
| break; |
| case rr::GEOMETRYSHADEROUTPUTTYPE_TRIANGLE_STRIP: |
| drawGeometryShaderOutputAsPrimitives<PRIMITIVETYPE_TRIANGLE_STRIP>( |
| state, renderTarget, program, &emitted[primitiveBegin], primitiveEnd - primitiveBegin, vpalloc); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| |
| // Next primitive |
| primitiveBegin = primitiveEnd + 1; |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * Assembles, tesselates, runs geometry shader and draws primitives of any type from vertex list. |
| *//*--------------------------------------------------------------------*/ |
| template <PrimitiveType DrawPrimitiveType> |
| void drawAsPrimitives(const RenderState &state, const RenderTarget &renderTarget, const Program &program, |
| VertexPacket *const *vertices, int numVertices, DrawContext &drawContext, |
| VertexPacketAllocator &vpalloc) |
| { |
| // Assemble primitives (deconstruct stips & loops) |
| const size_t assemblerPrimitiveCount = |
| PrimitiveTypeTraits<DrawPrimitiveType>::Assembler::getPrimitiveCount(numVertices); |
| std::vector<typename PrimitiveTypeTraits<DrawPrimitiveType>::Type> inputPrimitives(assemblerPrimitiveCount); |
| |
| PrimitiveTypeTraits<DrawPrimitiveType>::Assembler::exec(inputPrimitives.begin(), vertices, (size_t)numVertices, |
| state.provokingVertexConvention); |
| |
| // Tesselate |
| //if (state.tesselation) |
| // primList = state.tesselation.exec(primList); |
| |
| // Geometry shader |
| if (program.geometryShader) |
| { |
| // If there is an active geometry shader, it will convert any primitive type to basic types |
| drawWithGeometryShader<DrawPrimitiveType>(state, renderTarget, program, inputPrimitives, drawContext); |
| } |
| else |
| { |
| std::vector<typename PrimitiveTypeTraits<DrawPrimitiveType>::BaseType> basePrimitives; |
| |
| // convert types from X_adjacency to X |
| convertPrimitiveToBaseType(basePrimitives, inputPrimitives); |
| |
| // Make shared vertices distinct. Needed for that the translation to screen space happens only once per vertex, and for flatshading |
| makeSharedVerticesDistinct(basePrimitives, vpalloc); |
| |
| // A primitive ID will be generated even if no geometry shader is active |
| generatePrimitiveIDs(basePrimitives, drawContext); |
| |
| // Draw as a basic type |
| drawBasicPrimitives(state, renderTarget, program, basePrimitives, vpalloc); |
| } |
| } |
| |
| bool isValidCommand(const DrawCommand &command, int numInstances) |
| { |
| // numInstances should be valid |
| if (numInstances < 0) |
| return false; |
| |
| // Shaders should have the same varyings |
| if (command.program.geometryShader) |
| { |
| if (command.program.vertexShader->getOutputs() != command.program.geometryShader->getInputs()) |
| return false; |
| |
| if (command.program.geometryShader->getOutputs() != command.program.fragmentShader->getInputs()) |
| return false; |
| } |
| else |
| { |
| if (command.program.vertexShader->getOutputs() != command.program.fragmentShader->getInputs()) |
| return false; |
| } |
| |
| // Shader input/output types are set |
| for (size_t varyingNdx = 0; varyingNdx < command.program.vertexShader->getInputs().size(); ++varyingNdx) |
| if (command.program.vertexShader->getInputs()[varyingNdx].type != GENERICVECTYPE_FLOAT && |
| command.program.vertexShader->getInputs()[varyingNdx].type != GENERICVECTYPE_INT32 && |
| command.program.vertexShader->getInputs()[varyingNdx].type != GENERICVECTYPE_UINT32) |
| return false; |
| for (size_t varyingNdx = 0; varyingNdx < command.program.vertexShader->getOutputs().size(); ++varyingNdx) |
| if (command.program.vertexShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_FLOAT && |
| command.program.vertexShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_INT32 && |
| command.program.vertexShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_UINT32) |
| return false; |
| |
| for (size_t varyingNdx = 0; varyingNdx < command.program.fragmentShader->getInputs().size(); ++varyingNdx) |
| if (command.program.fragmentShader->getInputs()[varyingNdx].type != GENERICVECTYPE_FLOAT && |
| command.program.fragmentShader->getInputs()[varyingNdx].type != GENERICVECTYPE_INT32 && |
| command.program.fragmentShader->getInputs()[varyingNdx].type != GENERICVECTYPE_UINT32) |
| return false; |
| for (size_t varyingNdx = 0; varyingNdx < command.program.fragmentShader->getOutputs().size(); ++varyingNdx) |
| if (command.program.fragmentShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_FLOAT && |
| command.program.fragmentShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_INT32 && |
| command.program.fragmentShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_UINT32) |
| return false; |
| |
| if (command.program.geometryShader) |
| { |
| for (size_t varyingNdx = 0; varyingNdx < command.program.geometryShader->getInputs().size(); ++varyingNdx) |
| if (command.program.geometryShader->getInputs()[varyingNdx].type != GENERICVECTYPE_FLOAT && |
| command.program.geometryShader->getInputs()[varyingNdx].type != GENERICVECTYPE_INT32 && |
| command.program.geometryShader->getInputs()[varyingNdx].type != GENERICVECTYPE_UINT32) |
| return false; |
| for (size_t varyingNdx = 0; varyingNdx < command.program.geometryShader->getOutputs().size(); ++varyingNdx) |
| if (command.program.geometryShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_FLOAT && |
| command.program.geometryShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_INT32 && |
| command.program.geometryShader->getOutputs()[varyingNdx].type != GENERICVECTYPE_UINT32) |
| return false; |
| } |
| |
| // Enough vertex inputs? |
| if ((size_t)command.numVertexAttribs < command.program.vertexShader->getInputs().size()) |
| return false; |
| |
| // There is a fragment output sink for each output? |
| if ((size_t)command.renderTarget.getNumColorBuffers() < command.program.fragmentShader->getOutputs().size()) |
| return false; |
| |
| // All destination buffers should have same number of samples and same size |
| for (int outputNdx = 0; outputNdx < command.renderTarget.getNumColorBuffers(); ++outputNdx) |
| { |
| if (getBufferSize(command.renderTarget.getColorBuffer(0)) != |
| getBufferSize(command.renderTarget.getColorBuffer(outputNdx))) |
| return false; |
| |
| if (command.renderTarget.getNumSamples() != command.renderTarget.getColorBuffer(outputNdx).getNumSamples()) |
| return false; |
| } |
| |
| // All destination buffers should have same basic type as matching fragment output |
| for (size_t varyingNdx = 0; varyingNdx < command.program.fragmentShader->getOutputs().size(); ++varyingNdx) |
| { |
| const tcu::TextureChannelClass colorbufferClass = |
| tcu::getTextureChannelClass(command.renderTarget.getColorBuffer((int)varyingNdx).raw().getFormat().type); |
| const GenericVecType colorType = |
| (colorbufferClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER) ? |
| (rr::GENERICVECTYPE_INT32) : |
| ((colorbufferClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER) ? (rr::GENERICVECTYPE_UINT32) : |
| (rr::GENERICVECTYPE_FLOAT)); |
| |
| if (command.program.fragmentShader->getOutputs()[varyingNdx].type != colorType) |
| return false; |
| } |
| |
| // Integer values are flatshaded |
| for (size_t outputNdx = 0; outputNdx < command.program.vertexShader->getOutputs().size(); ++outputNdx) |
| { |
| if (!command.program.vertexShader->getOutputs()[outputNdx].flatshade && |
| (command.program.vertexShader->getOutputs()[outputNdx].type == GENERICVECTYPE_INT32 || |
| command.program.vertexShader->getOutputs()[outputNdx].type == GENERICVECTYPE_UINT32)) |
| return false; |
| } |
| if (command.program.geometryShader) |
| for (size_t outputNdx = 0; outputNdx < command.program.geometryShader->getOutputs().size(); ++outputNdx) |
| { |
| if (!command.program.geometryShader->getOutputs()[outputNdx].flatshade && |
| (command.program.geometryShader->getOutputs()[outputNdx].type == GENERICVECTYPE_INT32 || |
| command.program.geometryShader->getOutputs()[outputNdx].type == GENERICVECTYPE_UINT32)) |
| return false; |
| } |
| |
| // Draw primitive is valid for geometry shader |
| if (command.program.geometryShader) |
| { |
| if (command.program.geometryShader->getInputType() == rr::GEOMETRYSHADERINPUTTYPE_POINTS && |
| command.primitives.getPrimitiveType() != PRIMITIVETYPE_POINTS) |
| return false; |
| |
| if (command.program.geometryShader->getInputType() == rr::GEOMETRYSHADERINPUTTYPE_LINES && |
| (command.primitives.getPrimitiveType() != PRIMITIVETYPE_LINES && |
| command.primitives.getPrimitiveType() != PRIMITIVETYPE_LINE_STRIP && |
| command.primitives.getPrimitiveType() != PRIMITIVETYPE_LINE_LOOP)) |
| return false; |
| |
| if (command.program.geometryShader->getInputType() == rr::GEOMETRYSHADERINPUTTYPE_TRIANGLES && |
| (command.primitives.getPrimitiveType() != PRIMITIVETYPE_TRIANGLES && |
| command.primitives.getPrimitiveType() != PRIMITIVETYPE_TRIANGLE_STRIP && |
| command.primitives.getPrimitiveType() != PRIMITIVETYPE_TRIANGLE_FAN)) |
| return false; |
| |
| if (command.program.geometryShader->getInputType() == rr::GEOMETRYSHADERINPUTTYPE_LINES_ADJACENCY && |
| (command.primitives.getPrimitiveType() != PRIMITIVETYPE_LINES_ADJACENCY && |
| command.primitives.getPrimitiveType() != PRIMITIVETYPE_LINE_STRIP_ADJACENCY)) |
| return false; |
| |
| if (command.program.geometryShader->getInputType() == rr::GEOMETRYSHADERINPUTTYPE_TRIANGLES_ADJACENCY && |
| (command.primitives.getPrimitiveType() != PRIMITIVETYPE_TRIANGLES_ADJACENCY && |
| command.primitives.getPrimitiveType() != PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| RenderTarget::RenderTarget(const MultisamplePixelBufferAccess &colorMultisampleBuffer, |
| const MultisamplePixelBufferAccess &depthMultisampleBuffer, |
| const MultisamplePixelBufferAccess &stencilMultisampleBuffer) |
| : m_numColorBuffers(1) |
| , m_depthBuffer(MultisamplePixelBufferAccess::fromMultisampleAccess( |
| tcu::getEffectiveDepthStencilAccess(depthMultisampleBuffer.raw(), tcu::Sampler::MODE_DEPTH))) |
| , m_stencilBuffer(MultisamplePixelBufferAccess::fromMultisampleAccess( |
| tcu::getEffectiveDepthStencilAccess(stencilMultisampleBuffer.raw(), tcu::Sampler::MODE_STENCIL))) |
| { |
| m_colorBuffers[0] = colorMultisampleBuffer; |
| } |
| |
| int RenderTarget::getNumSamples(void) const |
| { |
| DE_ASSERT(m_numColorBuffers > 0); |
| return m_colorBuffers[0].getNumSamples(); |
| } |
| |
| DrawIndices::DrawIndices(const uint32_t *ptr, int baseVertex_) |
| : indices(ptr) |
| , indexType(INDEXTYPE_UINT32) |
| , baseVertex(baseVertex_) |
| { |
| } |
| |
| DrawIndices::DrawIndices(const uint16_t *ptr, int baseVertex_) |
| : indices(ptr) |
| , indexType(INDEXTYPE_UINT16) |
| , baseVertex(baseVertex_) |
| { |
| } |
| |
| DrawIndices::DrawIndices(const uint8_t *ptr, int baseVertex_) |
| : indices(ptr) |
| , indexType(INDEXTYPE_UINT8) |
| , baseVertex(baseVertex_) |
| { |
| } |
| |
| DrawIndices::DrawIndices(const void *ptr, IndexType type, int baseVertex_) |
| : indices(ptr) |
| , indexType(type) |
| , baseVertex(baseVertex_) |
| { |
| } |
| |
| PrimitiveList::PrimitiveList(PrimitiveType primitiveType, int numElements, const int firstElement) |
| : m_primitiveType(primitiveType) |
| , m_numElements(numElements) |
| , m_indices(DE_NULL) |
| , m_indexType(INDEXTYPE_LAST) |
| , m_baseVertex(firstElement) |
| { |
| DE_ASSERT(numElements >= 0 && "Invalid numElements"); |
| DE_ASSERT(firstElement >= 0 && "Invalid firstElement"); |
| } |
| |
| PrimitiveList::PrimitiveList(PrimitiveType primitiveType, int numElements, const DrawIndices &indices) |
| : m_primitiveType(primitiveType) |
| , m_numElements((size_t)numElements) |
| , m_indices(indices.indices) |
| , m_indexType(indices.indexType) |
| , m_baseVertex(indices.baseVertex) |
| { |
| DE_ASSERT(numElements >= 0 && "Invalid numElements"); |
| } |
| |
| size_t PrimitiveList::getIndex(size_t elementNdx) const |
| { |
| // indices == DE_NULL interpreted as command.indices = [first (=baseVertex) + 0, first + 1, first + 2...] |
| if (m_indices) |
| { |
| int index = m_baseVertex + (int)readIndexArray(m_indexType, m_indices, elementNdx); |
| DE_ASSERT(index >= 0); // do not access indices < 0 |
| |
| return (size_t)index; |
| } |
| else |
| return (size_t)(m_baseVertex) + elementNdx; |
| } |
| |
| bool PrimitiveList::isRestartIndex(size_t elementNdx, uint32_t restartIndex) const |
| { |
| // implicit index or explicit index (without base vertex) equals restart |
| if (m_indices) |
| return readIndexArray(m_indexType, m_indices, elementNdx) == restartIndex; |
| else |
| return elementNdx == (size_t)restartIndex; |
| } |
| |
| Renderer::Renderer(void) |
| { |
| } |
| |
| Renderer::~Renderer(void) |
| { |
| } |
| |
| void Renderer::draw(const DrawCommand &command) const |
| { |
| drawInstanced(command, 1); |
| } |
| |
| void Renderer::drawInstanced(const DrawCommand &command, int numInstances) const |
| { |
| // Do not run bad commands |
| { |
| const bool validCommand = isValidCommand(command, numInstances); |
| if (!validCommand) |
| { |
| DE_ASSERT(false); |
| return; |
| } |
| } |
| |
| // Do not draw if nothing to draw |
| { |
| if (command.primitives.getNumElements() == 0 || numInstances == 0) |
| return; |
| } |
| |
| // Prepare transformation |
| |
| const size_t numVaryings = command.program.vertexShader->getOutputs().size(); |
| VertexPacketAllocator vpalloc(numVaryings); |
| std::vector<VertexPacket *> vertexPackets = vpalloc.allocArray(command.primitives.getNumElements()); |
| DrawContext drawContext; |
| |
| for (int instanceID = 0; instanceID < numInstances; ++instanceID) |
| { |
| // Each instance has its own primitives |
| drawContext.primitiveID = 0; |
| |
| for (size_t elementNdx = 0; elementNdx < command.primitives.getNumElements(); ++elementNdx) |
| { |
| int numVertexPackets = 0; |
| |
| // collect primitive vertices until restart |
| |
| while (elementNdx < command.primitives.getNumElements() && |
| !(command.state.restart.enabled && |
| command.primitives.isRestartIndex(elementNdx, command.state.restart.restartIndex))) |
| { |
| // input |
| vertexPackets[numVertexPackets]->instanceNdx = instanceID; |
| vertexPackets[numVertexPackets]->vertexNdx = (int)command.primitives.getIndex(elementNdx); |
| |
| // output |
| vertexPackets[numVertexPackets]->pointSize = |
| command.state.point.pointSize; // default value from the current state |
| vertexPackets[numVertexPackets]->position = tcu::Vec4(0, 0, 0, 0); // no undefined values |
| |
| ++numVertexPackets; |
| ++elementNdx; |
| } |
| |
| // Duplicated restart shade |
| if (numVertexPackets == 0) |
| continue; |
| |
| // \todo Vertex cache? |
| |
| // Transform vertices |
| |
| command.program.vertexShader->shadeVertices(command.vertexAttribs, &vertexPackets[0], numVertexPackets); |
| |
| // Draw primitives |
| |
| switch (command.primitives.getPrimitiveType()) |
| { |
| case PRIMITIVETYPE_TRIANGLES: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_TRIANGLES>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_TRIANGLE_STRIP: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_TRIANGLE_STRIP>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, |
| vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_TRIANGLE_FAN: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_TRIANGLE_FAN>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_LINES: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_LINES>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_LINE_STRIP: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_LINE_STRIP>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_LINE_LOOP: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_LINE_LOOP>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_POINTS: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_POINTS>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_LINES_ADJACENCY: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_LINES_ADJACENCY>(command.state, command.renderTarget, command.program, |
| &vertexPackets[0], numVertexPackets, drawContext, |
| vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_LINE_STRIP_ADJACENCY: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_LINE_STRIP_ADJACENCY>(command.state, command.renderTarget, |
| command.program, &vertexPackets[0], |
| numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_TRIANGLES_ADJACENCY: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_TRIANGLES_ADJACENCY>(command.state, command.renderTarget, |
| command.program, &vertexPackets[0], |
| numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| case PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY: |
| { |
| drawAsPrimitives<PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY>(command.state, command.renderTarget, |
| command.program, &vertexPackets[0], |
| numVertexPackets, drawContext, vpalloc); |
| break; |
| } |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| } |
| } |
| |
| } // namespace rr |