Revision control

Copy as Markdown

Other Tools

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//-----------------------------------------------------------------------------
//
//
// Description:
// Contains HW Vertex Buffer and Builder class implementations
//
//
// Notes:
//
// +--------------------------------------+
// | |
// | Start Stratum |
// 1 | |
// | |
// +--------------------------------------+
// 2 |======================================|
// +--------------------------------------+
// | / \ / \ |
// | / \ / \ |
// | A / B \ C / D \ E |
// 3 | / \ / \ |
// | / \ / \ |
// | / \ / \ |
// | / \ / \ |
// +--------------------------------------+
// | \ / \ / |
// | \ / \ / |
// 4 | F \ G / H \ I / J |
// | \ / \ / |
// +--------------------------------------+
// 5 |======================================|
// +--------------------------------------+
// 6 |======================================|
// +--------------------------------------+
// | |
// | |
// 7 | Stop Stratum |
// | |
// | |
// +--------------------------------------+
//
//
// Strata & complement mode.
//
// The anti-aliased HW rasterizer produces a series of "strata" where
// each strata can be a complex span rendered using lines (#'s 2,5,6) or
// a series of trapezoids (#'s 3 & 4.) In normal mode the trapezoid
// regions B,D,G,I are filled in.
//
// Complement mode complicates things. Complex spans are relatively easy
// because we get the whole line's worth of data at once. Trapezoids are
// more complex because we get B,D,G and I separately. We handle this by
// tracking the current stratum and finishing the last incomplete
// trapezoid stratum when a new stratum begins. Regions E & J finish
// trapezoid strata. We also need to add rectangles at the beginning and
// end of the geometry (start and stop) to fill out the complement
// region.
//
// This is implemented like so:
//
// 1. Strata are generated from top to bottom without gaps.
// 2. Before drawing any lines or trapezoids call
// PrepareStratum(a, b, fTrapezoid) where a & b are the extent of
// the current stratum and fTrapezoid is true if you are drawing
// a trapezoid. This will take care of creating the start
// stratum and/or finishing a trapezoid stratum if necessary.
// 3. When completely done call EndBuildingOutside() which will
// close a pending trapezoid and/or produce the stop stratum.
//
//-----------------------------------------------------------------------------
const FORCE_TRIANGLES: bool = true;
//+----------------------------------------------------------------------------
//
// Constants to control when we stop waffling because the tiles are too
// small to make a difference.
//
// Future Consideration: can produce an excessive number of triangles.
// How we mitigate or handle this could be improved. Right now we stop
// waffling if the waffle size is less than a quarter-pixel.
// Two big improvements that could be made are:
// - multipacking very small textures (but note that we cannot rely
// on prefiltering to ensure that small screen space means small texture
// source)
// - clipping primitives to approximately the screen size
//
//-----------------------------------------------------------------------------
//const c_rMinWaffleWidthPixels: f32 = 0.25;
const FLOAT_ZERO: f32 = 0.;
const FLOAT_ONE: f32 = 1.;
//+----------------------------------------------------------------------------
//
// Class: CHwVertexBuffer and CHwTVertexBuffer<class TVertex>
//
// Synopsis: This class accumulates geometry data for a primitive
//
//-----------------------------------------------------------------------------
use crate::{types::*, geometry_sink::IGeometrySink, aacoverage::c_nShiftSizeSquared, OutputVertex, nullable_ref::Ref};
//+----------------------------------------------------------------------------
//
// Class: CHwVertexBuffer::Builder
//
// Synopsis: Base vertex builder class
//
// Responsibilities:
// - Given ordered basic vertex information expand/convert/pass-thru
// to vertex buffer (Basic vertex information is minimal vertex
// information sent from the caller that may or may not have been
// passed thru a tessellator.)
// - Choosing vertex format from a minimal required vertex format
//
// Not responsible for:
// - Allocating space in vertex buffer
//
// Inputs required:
// - Key and data to translate input basic vertex info to full vertex data
// - Vertex info from tessellation (or other Geometry Generator)
// - Vertex buffer to send output to
//
/*pub struct CHwVertexBufferBuilder /* : public IGeometrySink */
{
/*
public:
static HRESULT Create(
MilVertexFormat vfIn,
MilVertexFormat vfOut,
MilVertexFormatAttribute vfaAntiAliasScaleLocation,
__in_ecount_opt(1) CHwPipeline *pPipeline,
__in_ecount_opt(1) CD3DDeviceLevel1 *pDevice,
__in_ecount(1) CBufferDispenser *pBufferDispenser,
__deref_out_ecount(1) CHwVertexBuffer::Builder **ppVertexBufferBuilder
);
virtual ~Builder()
{
#if DBG
Assert(!m_fDbgDestroyed);
m_fDbgDestroyed = true;
#endif DBG
}
//+------------------------------------------------------------------------
//
// Member: SetConstantMapping
//
// Synopsis: Use this method to specify that the given color source for
// the given vertex destination is constant (won't differ per
// vertex)
//
//-------------------------------------------------------------------------
virtual HRESULT SetConstantMapping(
MilVertexFormatAttribute mvfaDestination,
__in_ecount(1) const CHwConstantColorSource *pConstCS
) PURE;
//+------------------------------------------------------------------------
//
// Member: FinalizeMappings
//
// Synopsis: Use this method to let builder know that all mappings have
// been sent
//
//-------------------------------------------------------------------------
virtual HRESULT FinalizeMappings(
) PURE;
//+------------------------------------------------------------------------
//
// Member: SetOutsideBounds
//
// Synopsis: Enables rendering zero-alpha geometry outside of the input
// shape but within the given bounding rectangle, if fNeedInside
// isn't true then it doesn't render geometry with full alpha.
//
//-------------------------------------------------------------------------
virtual void SetOutsideBounds(
__in_ecount_opt(1) const CMILSurfaceRect *prcBounds,
bool fNeedInside
) PURE;
//+------------------------------------------------------------------------
//
// Member: HasOutsideBounds
//
// Synopsis: Returns true if outside bounds have been set.
//
//-------------------------------------------------------------------------
virtual bool HasOutsideBounds() const PURE;
//+------------------------------------------------------------------------
//
// Member: BeginBuilding
//
// Synopsis: This method lets the builder know it should start from a
// clean slate
//
//-------------------------------------------------------------------------
virtual HRESULT BeginBuilding(
) PURE;
//+------------------------------------------------------------------------
//
// Member: EndBuilding
//
// Synopsis: Use this method to let the builder know that all of the
// vertex data has been sent
//
//-------------------------------------------------------------------------
virtual HRESULT EndBuilding(
__deref_opt_out_ecount(1) CHwVertexBuffer **ppVertexBuffer
) PURE;
//+------------------------------------------------------------------------
//
// Member: FlushReset
//
// Synopsis: Send pending state and geometry to the device and reset
// the vertex buffer.
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE HRESULT FlushReset()
{
return FlushInternal(NULL);
}
//
// Currently all CHwVertexBuffer::Builder are supposed to be allocated via
// a CBufferDispenser.
//
DECLARE_BUFFERDISPENSER_DELETE
protected:
Builder()
{
m_mvfIn = MILVFAttrNone;
#if DBG
m_mvfDbgOut = MILVFAttrNone;
#endif
m_mvfaAntiAliasScaleLocation = MILVFAttrNone;
m_pPipelineNoRef = NULL;
m_pDeviceNoRef = NULL;
#if DBG
m_fDbgDestroyed = false;
#endif DBG
}
//+------------------------------------------------------------------------
//
// Member: FlushInternal
//
// Synopsis: Send any pending state and geometry to the device.
// If the optional argument is NULL then reset the
// vertex buffer.
// If the optional argument is non-NULL AND we have
// not yet flushed the vertex buffer return the vertex
// buffer.
//
//-------------------------------------------------------------------------
virtual HRESULT FlushInternal(
__deref_opt_out_ecount_opt(1) CHwVertexBuffer **ppVertexBuffer
) PURE;
CHwPipeline *m_pPipelineNoRef;
CD3DDeviceLevel1 *m_pDeviceNoRef;
MilVertexFormat m_mvfIn; // Vertex fields that are pre-generated
#if DBG
MilVertexFormat m_mvfDbgOut; // Output format of the vertex
#endif
MilVertexFormat m_mvfGenerated; // Vertex fields that are dynamically
// generated by this builder
MilVertexFormatAttribute m_mvfaAntiAliasScaleLocation; // Vertex field that
// contains PPAA
// falloff factor
#if DBG
private:
bool m_fDbgDestroyed; // Used to check single Release pattern
#endif DBG
*/
}*/
#[derive(Default)]
pub struct CD3DVertexXYZDUV2 {
x: f32,
y: f32,
//Z: f32,
coverage: f32,
/*U0: f32, V0: f32,
U1: f32, V1: f32,*/
}
pub type CHwVertexBuffer<'z> = CHwTVertexBuffer<'z, OutputVertex>;
#[derive(Default)]
pub struct CHwTVertexBuffer<'z, TVertex>
{
//m_rgIndices: DynArray<WORD>, // Dynamic array of indices
//m_pBuilder: Rc<CHwTVertexBufferBuilder<TVertex>>,
/*
#if DBG
public:
CHwTVertexBuffer()
{
m_fDbgNonLineSegmentTriangleStrip = false;
}
#endif
protected:
//+------------------------------------------------------------------------
//
// Member: Reset
//
// Synopsis: Mark the beginning of a new list of vertices; the existing
// list is discarded
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE void Reset(
__in_ecount(1) Builder *pVBB
)
{
#if DBG
m_fDbgNonLineSegmentTriangleStrip = false;
#endif
m_rgIndices.SetCount(0);
m_rgVerticesTriList.SetCount(0);
m_rgVerticesTriStrip.SetCount(0);
m_rgVerticesLineList.SetCount(0);
m_rgVerticesNonIndexedTriList.SetCount(0);
m_pBuilder = pVBB;
}
//+------------------------------------------------------------------------
//
// Member: AddNonIndexedTriListVertices
//
// Synopsis: Reserve space for consecutive vertices and return start
// index
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE HRESULT AddNonIndexedTriListVertices(
UINT uCount,
__deref_ecount(uCount) TVertex **ppVertices
);
//+------------------------------------------------------------------------
//
// Member: AddTriListVertices
//
// Synopsis: Reserve space for consecutive vertices and return start
// index
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE HRESULT AddTriListVertices(
UINT uDelta,
__deref_ecount(uDelta) TVertex **ppVertices,
__out_ecount(1) WORD *pwIndexStart
);
//+------------------------------------------------------------------------
//
// Member: AddTriStripVertices
//
// Synopsis: Reserve space for consecutive vertices and return start
// index
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE HRESULT AddTriStripVertices(
UINT uCount,
__deref_ecount(uCount) TVertex **ppVertices
);
//+------------------------------------------------------------------------
//
// Member: AddLineListVertices
//
// Synopsis: Reserve space for consecutive vertices and return start
// index
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE HRESULT AddLineListVertices(
UINT uCount,
__deref_ecount(uCount) TVertex **ppVertices
);
public:
//+------------------------------------------------------------------------
//
// Member: AddLine implements ILineSink<PointXYA>
//
// Synopsis: Add a line given two points with x, y, & alpha.
//
//-------------------------------------------------------------------------
HRESULT AddLine(
__in_ecount(1) const PointXYA &v0,
__in_ecount(1) const PointXYA &v1
);
//+------------------------------------------------------------------------
//
// Member: AddTriangle implements ITriangleSink<PointXYA>
//
// Synopsis: Add a triangle given three points with x, y, & alpha.
//
//-------------------------------------------------------------------------
HRESULT AddTriangle(
__in_ecount(1) const PointXYA &v0,
__in_ecount(1) const PointXYA &v1,
__in_ecount(1) const PointXYA &v2
);
// Re-introduce parent AddTriangle(WORD,WORD,WORD) into this scope.
using CHwVertexBuffer::AddTriangle;
//+------------------------------------------------------------------------
//
// Member: AddLineAsTriangleStrip
//
// Synopsis: Add a horizontal line using a trinagle strip
//
//-------------------------------------------------------------------------
HRESULT AddLineAsTriangleStrip(
__in_ecount(1) const TVertex *pBegin, // Begin
__in_ecount(1) const TVertex *pEnd // End
);
//+------------------------------------------------------------------------
//
// Member: SendVertexFormat
//
// Synopsis: Send contained vertex format to device
//
//-------------------------------------------------------------------------
HRESULT SendVertexFormat(
__inout_ecount(1) CD3DDeviceLevel1 *pDevice
) const;
//+------------------------------------------------------------------------
//
// Member: DrawPrimitive
//
// Synopsis: Send the geometry data to the device and execute rendering
//
//-------------------------------------------------------------------------
HRESULT DrawPrimitive(
__inout_ecount(1) CD3DDeviceLevel1 *pDevice
) const;
protected:
//+------------------------------------------------------------------------
//
// Member: GetNumTriListVertices
//
// Synopsis: Return current number of vertices
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE DWORD GetNumTriListVertices() const
{
return m_rgVerticesTriList.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetTriListVertices
//
// Synopsis: Return pointer to beginning of vertex list and their count
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE void GetTriListVertices(
__deref_out_ecount_full(*puNumVertices) TVertex **ppVertices,
__out_ecount(1) UINT * puNumVertices
)
{
*ppVertices = m_rgVerticesTriList.GetDataBuffer();
*puNumVertices = m_rgVerticesTriList.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetNumNonIndexedTriListVertices
//
// Synopsis: Return current number of vertices
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE DWORD GetNumNonIndexedTriListVertices() const
{
return m_rgVerticesNonIndexedTriList.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetNonIndexedTriListVertices
//
// Synopsis: Return pointer to beginning of vertex list and their count
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE void GetNonIndexedTriListVertices(
__deref_out_ecount_full(*puNumVertices) TVertex **ppVertices,
__out_ecount(1) UINT * puNumVertices
)
{
*ppVertices = m_rgVerticesNonIndexedTriList.GetDataBuffer();
*puNumVertices = m_rgVerticesNonIndexedTriList.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetNumTriStripVertices
//
// Synopsis: Return current number of vertices
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE DWORD GetNumTriStripVertices() const
{
return m_rgVerticesTriStrip.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetTriStripVertices
//
// Synopsis: Return pointer to beginning of vertex list and their count
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE void GetTriStripVertices(
__deref_out_ecount_full(*puNumVertices) TVertex **ppVertices,
__out_ecount(1) UINT *puNumVertices
)
{
*ppVertices = m_rgVerticesTriStrip.GetDataBuffer();
*puNumVertices = m_rgVerticesTriStrip.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetNumLineListVertices
//
// Synopsis: Return current number of vertices
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE DWORD GetNumLineListVertices() const
{
return m_rgVerticesLineList.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetLineListVertices
//
// Synopsis: Return pointer to beginning of vertex list and their count
//
//-------------------------------------------------------------------------
MIL_FORCEINLINE void GetLineListVertices(
__deref_out_ecount_full(*puNumVertices) TVertex **ppVertices,
__out_ecount(1) UINT * puNumVertices
)
{
*ppVertices = m_rgVerticesLineList.GetDataBuffer();
*puNumVertices = m_rgVerticesLineList.GetCount();
}
//+------------------------------------------------------------------------
//
// Member: GetLineListVertices
//
// Synopsis: Return pointer to beginning of vertex list
//
//-------------------------------------------------------------------------
*/
// Dynamic array of vertices for which all allocations are zeroed.
// XXX: the zero has been removed
//m_rgVerticesTriList: DynArray<TVertex>, // Indexed triangle list vertices
//m_rgVerticesNonIndexedTriList: DynArray<TVertex>, // Non-indexed triangle list vertices
m_rgVerticesTriList: DynArray<TVertex>, // Triangle strip vertices
//m_rgVerticesLineList: DynArray<TVertex>, // Linelist vertices
m_rgVerticesBuffer: Option<&'z mut [TVertex]>,
m_rgVerticesBufferOffset: usize,
#[cfg(debug_assertions)]
// In debug make a note if we add a triangle strip that doesn't have 6 vertices
// so that we can ensure that we only waffle 6-vertex tri strips.
m_fDbgNonLineSegmentTriangleStrip: bool,
subpixel_bias: f32,
}
impl<'z, TVertex: Default> CHwTVertexBuffer<'z, TVertex> {
pub fn new(rasterization_truncates: bool, output_buffer: Option<&'z mut [TVertex]>) -> Self {
CHwTVertexBuffer::<TVertex> {
subpixel_bias: if rasterization_truncates {
// 1/512 is 0.5 of a subpixel when using 8 bits of subpixel precision.
1./512.
} else {
0.
},
m_rgVerticesBuffer: output_buffer,
m_rgVerticesBufferOffset: 0,
..Default::default()
}
}
pub fn flush_output(&mut self) -> Box<[TVertex]> {
std::mem::take(&mut self.m_rgVerticesTriList).into_boxed_slice()
}
pub fn get_output_buffer_size(&self) -> Option<usize> {
if self.m_rgVerticesBuffer.is_some() {
Some(self.m_rgVerticesBufferOffset)
} else {
None
}
}
}
//+----------------------------------------------------------------------------
//
// Class: CHwTVertexMappings<class TVertex>
//
// Synopsis: Helper class that knows how to populate a vertex from the
// incoming basic per vertex data, like just X and Y
//
//-----------------------------------------------------------------------------
#[derive(Default)]
struct CHwTVertexMappings<TVertex>
{/*
public:
CHwTVertexMappings();
void SetPositionTransform(
__in_ecount(1) const MILMatrix3x2 &matPositionTransform
);
HRESULT SetConstantMapping(
MilVertexFormatAttribute mvfaDestination,
__in_ecount(1) const CHwConstantColorSource *pConstCS
);
void PointToUV(
__in_ecount(1) const MilPoint2F &ptIn,
__bound UINT uIndex,
__out_ecount(1) TVertex *pvOut
);
MIL_FORCEINLINE bool AreWaffling() const
{
return false;
}
private:
static const size_t s_numOfVertexTextureCoords
= NUM_OF_VERTEX_TEXTURE_COORDS(TVertex);
public:
MilVertexFormat m_mvfMapped;
MilColorF m_colorStatic;
MILMatrix3x2 m_matPos2DTransform;
MILMatrix3x2 m_rgmatPointToUV[s_numOfVertexTextureCoords];
CMilPointAndSizeF m_rgSubrect[s_numOfVertexTextureCoords];
WaffleModeFlags m_rgWaffleMode[s_numOfVertexTextureCoords];
*/
m_vStatic: TVertex,
subpixel_bias: f32,
}
impl<TVertex> CHwTVertexBuffer<'_, TVertex> {
pub fn Reset(&mut self,
/*pVBB: &mut CHwTVertexBufferBuilder<TVertex>*/
)
{
#[cfg(debug_assertions)]
{
self.m_fDbgNonLineSegmentTriangleStrip = false;
}
//self.m_rgIndices.SetCount(0);
//self.m_rgVerticesTriList.SetCount(0);
self.m_rgVerticesTriList.SetCount(0);
self.m_rgVerticesBufferOffset = 0;
//self.m_rgVerticesLineList.SetCount(0);
//self.m_rgVerticesNonIndexedTriList.SetCount(0);
//self.m_pBuilder = pVBB;
}
fn IsEmpty(&self) -> bool
{
return true
// && (self.m_rgIndices.GetCount() == 0)
//&& (self.m_rgVerticesLineList.GetCount() == 0)
&& (self.m_rgVerticesTriList.GetCount() == 0)
&& self.m_rgVerticesBufferOffset == 0
//&& (self.m_rgVerticesNonIndexedTriList.GetCount() == 0);
}
}
//+----------------------------------------------------------------------------
//
// Class: CHwTVertexBuffer<class TVertex>::Builder
//
// Synopsis: Implements CHwVertexBuffer::Builder for a particular vertex
// format
//
//-----------------------------------------------------------------------------
pub struct CHwTVertexBufferBuilder<'y, 'z, TVertex>
{
m_mvfIn: MilVertexFormat, // Vertex fields that are pre-generated
#[cfg(debug_assertions)]
m_mvfDbgOut: MilVertexFormat, // Output format of the vertex
m_mvfGenerated: MilVertexFormat, // Vertex fields that are dyn
m_mvfaAntiAliasScaleLocation: MilVertexFormatAttribute, // Vertex field that
// contains PPAA
// falloff factor
/*
public:
static MilVertexFormat GetOutVertexFormat();
static HRESULT Create(
__in_ecount(1) CHwTVertexBuffer<TVertex> *pVertexBuffer,
MilVertexFormat mvfIn,
MilVertexFormat mvfOut,
MilVertexFormatAttribute mvfaAntiAliasScaleLocation,
__inout_ecount(1) CBufferDispenser *pBufferDispenser,
__deref_out_ecount(1) typename CHwTVertexBuffer<TVertex>::Builder **ppVertexBufferBuilder
);
HRESULT SetConstantMapping(
MilVertexFormatAttribute mvfaDestination,
__in_ecount(1) const CHwConstantColorSource *pConstCS
);
void SetTransformMapping(
__in_ecount(1) const MILMatrix3x2 &mat2DTransform
);
HRESULT FinalizeMappings(
);
void SetOutsideBounds(
__in_ecount_opt(1) const CMILSurfaceRect *prcBounds,
bool fNeedInside
);
bool HasOutsideBounds() const
{
return NeedOutsideGeometry();
}
HRESULT BeginBuilding(
);
HRESULT AddVertex(
__in_ecount(1) const MilPoint2F &ptPosition,
// In: Vertex coordinates
__out_ecount(1) WORD *pIndex
// Out: The index of the new vertex
);
HRESULT AddIndexedVertices(
UINT cVertices, // In: number of vertices
__in_bcount(cVertices*uVertexStride) const void *pVertexBuffer, // In: vertex buffer containing the vertices
UINT uVertexStride, // In: size of each vertex
MilVertexFormat mvfFormat, // In: format of each vertex
UINT cIndices, // In: Number of indices
__in_ecount(cIndices) const UINT *puIndexBuffer // In: index buffer
);
HRESULT AddTriangle(
DWORD i1, // In: Index of triangle's first vertex
DWORD i2, // In: Index of triangle's second vertex
DWORD i3 // In: Index of triangle's third vertex
);
HRESULT AddComplexScan(
INT nPixelY,
// In: y coordinate in pixel space
__in_ecount(1) const CCoverageInterval *pIntervalSpanStart
// In: coverage segments
);
HRESULT AddParallelogram(
__in_ecount(4) const MilPoint2F *rgPosition
);
HRESULT AddTrapezoid(
float rPixelYTop, // In: y coordinate of top of trapezoid
float rPixelXTopLeft, // In: x coordinate for top left
float rPixelXTopRight, // In: x coordinate for top right
float rPixelYBottom, // In: y coordinate of bottom of trapezoid
float rPixelXBottomLeft, // In: x coordinate for bottom left
float rPixelXBottomRight, // In: x coordinate for bottom right
float rPixelXLeftDelta, // In: trapezoid expand radius for left edge
float rPixelXRightDelta // In: trapezoid expand radius for right edge
);
BOOL IsEmpty();
HRESULT EndBuilding(
__deref_opt_out_ecount(1) CHwVertexBuffer **ppVertexBuffer
);
HRESULT FlushInternal(
__deref_opt_out_ecount_opt(1) CHwVertexBuffer **ppVertexBuffer
);
private:
// Helpers that do AddTrapezoid. Same parameters
HRESULT AddTrapezoidStandard( float, float, float, float, float, float, float, float );
HRESULT AddTrapezoidWaffle( float, float, float, float, float, float, float, float );
HRESULT PrepareStratumSlow(
float rStratumTop,
float rStratumBottom,
bool fTrapezoid,
float rTrapezoidLeft,
float rTrapezoidRight
);
// Wrap up building of outside geometry.
HRESULT EndBuildingOutside();
DECLARE_BUFFERDISPENSER_NEW(CHwTVertexBuffer<TVertex>::Builder,
Mt(CHwTVertexBuffer_Builder));
Builder(
__in_ecount(1) CHwTVertexBuffer<TVertex> *pVertexBuffer
);
HRESULT SetupConverter(
MilVertexFormat mvfIn,
MilVertexFormat mvfOut,
MilVertexFormatAttribute mvfaAntiAliasScaleLocation
);
HRESULT RenderPrecomputedIndexedTriangles(
__range(1, SHORT_MAX) UINT cVertices,
__in_ecount(cVertices) const TVertex *rgoVertices,
__range(1, UINT_MAX) UINT cIndices,
__in_ecount(cIndices) const UINT *rguIndices
);
// Expands all vertices in the buffer.
void ExpandVertices();
// Has never been successfully used to declare a method or derived type...
/* typedef void (CHwTVertexBuffer<TVertex>::Builder::FN_ExpandVertices)(
UINT uCount,
TVertex *pVertex
);*/
// error C2143: syntax error : missing ';' before '*'
// typedef FN_ExpandVertices *PFN_ExpandVertices;
typedef void (CHwTVertexBuffer<TVertex>::Builder::* PFN_ExpandVertices)(
__range(1,UINT_MAX) UINT uCount,
__inout_ecount_full(uCount) TVertex *rgVertices
);
//
// Table of vertex expansion routines for common expansion cases:
// - There are entries for Z, Diffuse, and one set texture coordinates for
// a total of eight combinations.
// - Additionally there is a second set of entries for anti-aliasing
// falloff applied thru diffuse.
//
static const PFN_ExpandVertices sc_pfnExpandVerticesTable[8*2];
MIL_FORCEINLINE
void TransferAndOrExpandVerticesInline(
__range(1,UINT_MAX) UINT uCount,
__in_ecount(uCount) TVertex const * rgInputVertices,
__out_ecount(uCount) TVertex *rgOutputVertices,
MilVertexFormat mvfOut,
MilVertexFormatAttribute mvfaScaleByFalloff,
bool fInputOutputAreSameBuffer,
bool fTransformPosition
);
// FN_ExpandVertices ExpandVerticesFast
template <MilVertexFormat mvfOut, MilVertexFormatAttribute mvfaScaleByFalloff>
void ExpandVerticesFast(
__range(1,UINT_MAX) UINT uCount,
__inout_ecount_full(uCount) TVertex *rgVertices
)
{
TransferAndOrExpandVerticesInline(
uCount,
rgVertices,
rgVertices,
mvfOut,
mvfaScaleByFalloff,
true, // => fInputOutputAreSameBuffer
false // => fTransformPosition
);
}
// error C2146: syntax error : missing ';' before identifier 'ExpandVerticesGeneral'
// error C2501: 'CHwTVertexBufferBuilder<TVertex>::FN_ExpandVertices' : missing storage-class or type specifiers
// FN_ExpandVertices ExpandVerticesGeneral
// typename FN_ExpandVertices ExpandVerticesGeneral
// error C4346: 'CHwTVertexBufferBuilder<TVertex>::FN_ExpandVertices' : dependent name is not a type
// CHwTVertexBufferBuilder<TVertex>::FN_ExpandVertices ExpandVerticesGeneral
// Can't define methos here (unless not parameters are used).
// typename CHwTVertexBufferBuilder<TVertex>::FN_ExpandVertices ExpandVerticesGeneral
// FN_ExpandVertices ExpandVerticesGeneral
void ExpandVerticesGeneral(
__range(1,UINT_MAX) UINT uCount,
__inout_ecount_full(uCount) TVertex *rgVertices
)
{
TransferAndOrExpandVerticesInline(
uCount,
rgVertices,
rgVertices,
m_mvfGenerated,
m_mvfaAntiAliasScaleLocation,
true, // => fInputOutputAreSameBuffer
false // => fTransformPosition
);
}
void TransferAndExpandVerticesGeneral(
__range(1,UINT_MAX) UINT uCount,
__in_ecount(uCount) TVertex const *rgInputVertices,
__out_ecount_full(uCount) TVertex *rgOutputVertices,
bool fTransformPosition
)
{
TransferAndOrExpandVerticesInline(
uCount,
rgInputVertices,
rgOutputVertices,
m_mvfGenerated,
m_mvfaAntiAliasScaleLocation,
false, // => fInputOutputAreSameBuffer
fTransformPosition // => fTransformPosition
);
}
// FN_ExpandVertices ExpandVerticesInvalid
void ExpandVerticesInvalid(
__range(1,UINT_MAX) UINT uCount,
__inout_ecount_full(uCount) TVertex *rgVertices
)
{
RIP("Invalid ExpandVertices routine.");
}
//+------------------------------------------------------------------------
//
// Member: NeedCoverageGeometry
//
// Synopsis: True if we should create geometry for a particular
// coverage value.
//
//-------------------------------------------------------------------------
bool NeedCoverageGeometry(INT nCoverage) const;
//+------------------------------------------------------------------------
//
// Member: ReinterpretFloatAsDWORD
//
// Synopsis: Quicky helper to convert a float to a DWORD bitwise.
//
//-------------------------------------------------------------------------
static MIL_FORCEINLINE DWORD ReinterpretFloatAsDWORD(float c)
{
return reinterpret_cast<DWORD &>(c);
}
private:
MIL_FORCEINLINE bool AreWaffling() const
{
return m_map.AreWaffling();
}
void ViewportToPackedCoordinates(
__range(1,UINT_MAX / uGroupSize) UINT uGroupCount,
__inout_ecount(uGroupCount * uGroupSize) TVertex *pVertex,
__range(2,6) UINT uGroupSize,
/*__range(0,NUM_OF_VERTEX_TEXTURE_COORDS(TVertex)-1)*/ __bound UINT uIndex
);
void ViewportToPackedCoordinates(
__range(1,UINT_MAX / uGroupSize) UINT uGroupCount,
__inout_ecount(uGroupCount * uGroupSize) TVertex *pVertex,
__range(2,6) UINT uGroupSize
);
template<class TWaffler>
__out_ecount(1) typename TWaffler::ISink *
BuildWafflePipeline(
__out_xcount(NUM_OF_VERTEX_TEXTURE_COORDS(TVertex) * 2) TWaffler *wafflers,
__out_ecount(1) bool &fWafflersUsed
) const;
template<class TWaffler>
typename TWaffler::ISink *
BuildWafflePipeline(
__out_xcount(NUM_OF_VERTEX_TEXTURE_COORDS(TVertex) * 2) TWaffler *wafflers
) const
{
bool fNotUsed;
return BuildWafflePipeline(wafflers, fNotUsed);
}*/
m_pVB: &'y mut CHwTVertexBuffer<'z, TVertex>,
//m_pfnExpandVertices: PFN_ExpandVertices, // Method for expanding vertices
//m_rgoPrecomputedTriListVertices: *const TVertex,
//m_cPrecomputedTriListVertices: UINT,
//m_rguPrecomputedTriListIndices: *const UINT,
//m_cPrecomputedTriListIndices: UINT,
//m_map: CHwTVertexMappings<TVertex>,
// This is true if we had to flush the pipeline as we were getting
// geometry rather than just filling up a single vertex buffer.
m_fHasFlushed: bool,
// The next two members control the generation of the zero-alpha geometry
// outside the input geometry.
m_fNeedOutsideGeometry: bool,
m_fNeedInsideGeometry: bool,
m_rcOutsideBounds: CMILSurfaceRect, // Bounds for creation of outside geometry
/*
// Helpful m_rcOutsideBounds casts.
float OutsideLeft() const { return static_cast<float>(m_rcOutsideBounds.left); }
float OutsideRight() const { return static_cast<float>(m_rcOutsideBounds.right); }
float OutsideTop() const { return static_cast<float>(m_rcOutsideBounds.top); }
float OutsideBottom() const { return static_cast<float>(m_rcOutsideBounds.bottom); }
*/
// This interval (if we are doing outside) shows the location
// of the current stratum. It is initialized to [FLT_MAX, -FLT_MAX].
//
// If the current stratum is a complex span then
// m_rCurStratumBottom is set to the bottom of the stratum and
// m_rCurStratumTop is set to FLT_MAX.
//
// If the current stratum is a trapezoidal one, then
// m_rCurStratumBottom is its bottom and m_rCurStratumTop is its
// top.
m_rCurStratumTop: f32,
m_rCurStratumBottom: f32,
// If the current stratum is a trapezoidal one, following var stores
// right boundary of the last trapezoid handled by PrepareStratum.
// We need it to cloze the stratus properly.
m_rLastTrapezoidRight: f32,
// These are needed to implement outside geometry using triangle lists
m_rLastTrapezoidTopRight: f32,
m_rLastTrapezoidBottomRight: f32,
}
/*
//+----------------------------------------------------------------------------
//
// Member: CHwVertexBuffer::AddTriangle
//
// Synopsis: Add a triangle using the three indices given to the list
//
impl CHwVertexBuffer {
fn AddTriangle(
i1: WORD, // In: Index of triangle's first vertex
i2: WORD, // In: Index of triangle's second vertex
i3: WORD // In: Index of triangle's third vertex
) -> HRESULT
{
let hr: HRESULT = S_OK;
// Asserting indices < max vertex requires a debug only pure virtual method
// which is too much of a functionality change between retail and debug.
//
//
// Assert(i1 < GetNumTriListVertices());
// Assert(i2 < GetNumTriListVertices());
// Assert(i3 < GetNumTriListVertices());
WORD *pIndices;
IFC(m_rgIndices.AddMultiple(3, &pIndices));
pIndices[0] = i1;
pIndices[1] = i2;
pIndices[2] = i3;
Cleanup:
RRETURN(hr);
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::AddTriangle
//
// Synopsis: Add a triangle using given three points to the list
//
//-----------------------------------------------------------------------------
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::AddTriangle(
__in_ecount(1) const PointXYA &v0,
__in_ecount(1) const PointXYA &v1,
__in_ecount(1) const PointXYA &v2)
{
let hr: HRESULT = S_OK;
TVertex *pVertices;
hr = AddNonIndexedTriListVertices(3,&pVertices);
if (hr == E_OUTOFMEMORY)
{
DebugBreak ();
}
IFC(hr);
pVertices[0].ptPt.X = v0.x;
pVertices[0].ptPt.Y = v0.y;
pVertices[0].Diffuse = reinterpret_cast<const DWORD &>(v0.a);
pVertices[1].ptPt.X = v1.x;
pVertices[1].ptPt.Y = v1.y;
pVertices[1].Diffuse = reinterpret_cast<const DWORD &>(v1.a);
pVertices[2].ptPt.X = v2.x;
pVertices[2].ptPt.Y = v2.y;
pVertices[2].Diffuse = reinterpret_cast<const DWORD &>(v2.a);
Cleanup:
RRETURN(hr);
}
*/
impl CHwVertexBuffer<'_> {
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::AddLine
//
// Synopsis: Add a nominal width line using given two points to the list
//
//-----------------------------------------------------------------------------
fn AddLine(&mut self,
v0: &PointXYA,
v1: &PointXYA
) -> HRESULT
{
type TVertex = CD3DVertexXYZDUV2;
let hr = S_OK;
let pVertices: &mut [TVertex];
let mut rgScratchVertices: [TVertex; 2] = Default::default();
assert!(!(v0.y != v1.y));
let fUseTriangles = /*(v0.y < m_pBuilder->GetViewportTop() + 1) ||*/ FORCE_TRIANGLES;
//if (fUseTriangles)
//{
pVertices = &mut rgScratchVertices;
//}
//else
//{
//IFC!(AddLineListVertices(2, &pVertices));
//}
pVertices[0].x = v0.x;
pVertices[0].y = v0.y;
pVertices[0].coverage = v0.a;
pVertices[1].x = v1.x;
pVertices[1].y = v1.y;
pVertices[1].coverage = v1.a;
if (fUseTriangles)
{
IFC!(self.AddLineAsTriangleList(&pVertices[0],&pVertices[1]));
}
RRETURN!(hr);
}
}
/*
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::AddTriListVertices
//
// Synopsis: Reserve space for consecutive vertices and return start index
//
template <class TVertex>
MIL_FORCEINLINE
HRESULT
CHwTVertexBuffer<TVertex>::AddTriListVertices(
UINT uDelta,
__deref_ecount(uDelta) TVertex **ppVertices,
__out_ecount(1) WORD *pwIndexStart
)
{
HRESULT hr = S_OK;
Assert(ppVertices);
UINT uCount = static_cast<UINT>(m_rgVerticesTriList.GetCount());
if (uCount > SHRT_MAX)
{
IFC(WGXERR_INVALIDPARAMETER);
}
UINT newCount;
newCount = uDelta + uCount;
if (newCount > SHRT_MAX)
{
IFC(m_pBuilder->FlushReset());
uCount = 0;
newCount = uDelta;
}
if (newCount > m_rgVerticesTriList.GetCapacity())
{
IFC(m_rgVerticesTriList.ReserveSpace(uDelta));
}
m_rgVerticesTriList.SetCount(newCount);
*pwIndexStart = static_cast<WORD>(uCount);
*ppVertices = &m_rgVerticesTriList[uCount];
Cleanup:
RRETURN(hr);
}
*/
impl<TVertex: Clone + Default> CHwTVertexBuffer<'_, TVertex> {
fn AddTriVertices(&mut self, v0: TVertex, v1: TVertex, v2: TVertex) {
if let Some(output_buffer) = &mut self.m_rgVerticesBuffer {
let offset = self.m_rgVerticesBufferOffset;
if offset + 3 <= output_buffer.len() {
output_buffer[offset] = v0;
output_buffer[offset + 1] = v1;
output_buffer[offset + 2] = v2;
}
self.m_rgVerticesBufferOffset = offset + 3;
} else {
self.m_rgVerticesTriList.reserve(3);
self.m_rgVerticesTriList.push(v0);
self.m_rgVerticesTriList.push(v1);
self.m_rgVerticesTriList.push(v2);
}
}
fn AddTrapezoidVertices(&mut self, v0: TVertex, v1: TVertex, v2: TVertex, v3: TVertex) {
if let Some(output_buffer) = &mut self.m_rgVerticesBuffer {
let offset = self.m_rgVerticesBufferOffset;
if offset + 6 <= output_buffer.len() {
output_buffer[offset] = v0;
output_buffer[offset + 1] = v1.clone();
output_buffer[offset + 2] = v2.clone();
output_buffer[offset + 3] = v1;
output_buffer[offset + 4] = v2;
output_buffer[offset + 5] = v3;
}
self.m_rgVerticesBufferOffset = offset + 6;
} else {
self.m_rgVerticesTriList.reserve(6);
self.m_rgVerticesTriList.push(v0);
self.m_rgVerticesTriList.push(v1.clone());
self.m_rgVerticesTriList.push(v2.clone());
self.m_rgVerticesTriList.push(v1);
self.m_rgVerticesTriList.push(v2);
self.m_rgVerticesTriList.push(v3);
}
}
fn AddedNonLineSegment(&mut self) {
#[cfg(debug_assertions)]
{
self.m_fDbgNonLineSegmentTriangleStrip = true;
}
}
}
/*
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::AddNonIndexedTriListVertices
//
// Synopsis: Reserve space for triangle list vertices.
//
template <class TVertex>
MIL_FORCEINLINE
HRESULT
CHwTVertexBuffer<TVertex>::AddNonIndexedTriListVertices(
UINT uCount,
__deref_ecount(uCount) TVertex **ppVertices
)
{
HRESULT hr = S_OK;
UINT Count = static_cast<UINT>(m_rgVerticesNonIndexedTriList.GetCount());
UINT newCount = Count + uCount;
if (newCount > m_rgVerticesNonIndexedTriList.GetCapacity())
{
IFC(m_rgVerticesNonIndexedTriList.ReserveSpace(uCount));
}
m_rgVerticesNonIndexedTriList.SetCount(newCount);
*ppVertices = &m_rgVerticesNonIndexedTriList[Count];
Cleanup:
RRETURN(hr);
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::AddLineListVertices
//
// Synopsis: Reserve space for consecutive vertices
//
template <class TVertex>
MIL_FORCEINLINE
HRESULT
CHwTVertexBuffer<TVertex>::AddLineListVertices(
UINT uCount,
__deref_ecount(uCount) TVertex **ppVertices
)
{
HRESULT hr = S_OK;
Assert(ppVertices);
UINT Count = static_cast<UINT>(m_rgVerticesLineList.GetCount());
UINT newCount = Count + uCount;
if (newCount > m_rgVerticesLineList.GetCapacity())
{
IFC(m_rgVerticesLineList.ReserveSpace(uCount));
}
m_rgVerticesLineList.SetCount(newCount);
*ppVertices = &m_rgVerticesLineList[Count];
Cleanup:
RRETURN(hr);
}
//+----------------------------------------------------------------------------
//
// Class: CHwVertexBuffer::Builder
//
//-----------------------------------------------------------------------------
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::GetOutVertexFormat
//
// Synopsis: Return MIL vertex format covered by specific builders
//
//-----------------------------------------------------------------------------
template <>
MilVertexFormat
CHwTVertexBuffer<CD3DVertexXYZDUV2>::Builder::GetOutVertexFormat()
{
return (MILVFAttrXYZ | MILVFAttrDiffuse | MILVFAttrUV2);
}
template <>
MilVertexFormat
CHwTVertexBuffer<CD3DVertexXYZDUV8>::Builder::GetOutVertexFormat()
{
return (MILVFAttrXYZ | MILVFAttrDiffuse | MILVFAttrUV8);
}
template <>
MilVertexFormat
CHwTVertexBuffer<CD3DVertexXYZDUV6>::Builder::GetOutVertexFormat()
{
return (MILVFAttrXYZ | MILVFAttrDiffuse | MILVFAttrUV6);
}
template <>
MilVertexFormat
CHwTVertexBuffer<CD3DVertexXYZNDSUV4>::Builder::GetOutVertexFormat()
{
return (MILVFAttrXYZ |
MILVFAttrNormal |
MILVFAttrDiffuse |
MILVFAttrSpecular |
MILVFAttrUV4);
}
//+----------------------------------------------------------------------------
//
// Member: CHwVertexBuffer::Builder::Create
//
// Synopsis: Choose the appropriate final vertex format and instantiate the
// matching vertex builder
//
*/
pub type CHwVertexBufferBuilder<'y, 'z> = CHwTVertexBufferBuilder<'y, 'z, OutputVertex>;
impl<'y, 'z> CHwVertexBufferBuilder<'y, 'z> {
pub fn Create(
vfIn: MilVertexFormat,
vfOut: MilVertexFormat,
mvfaAntiAliasScaleLocation: MilVertexFormatAttribute,
pVertexBuffer: &'y mut CHwVertexBuffer<'z>,
/*pBufferDispenser: &CBufferDispenser*/
) -> CHwVertexBufferBuilder<'y, 'z>
{
CHwVertexBufferBuilder::CreateTemplate(pVertexBuffer, vfIn, vfOut, mvfaAntiAliasScaleLocation)
//let hr: HRESULT = S_OK;
//assert!(ppVertexBufferBuilder);
//*ppVertexBufferBuilder = None;
/*
if (!(vfOut & ~CHwTVertexBuffer<CD3DVertexXYZDUV2>::Builder::GetOutVertexFormat()))
{
CHwTVertexBuffer<CD3DVertexXYZDUV2> *pVB = pDevice->GetVB_XYZDUV2();
CHwTVertexBuffer<CD3DVertexXYZDUV2>::Builder *pVBB = NULL;
IFC(CHwTVertexBuffer<CD3DVertexXYZDUV2>::Builder::Create(
pVB,
vfIn,
vfOut,
mvfaAntiAliasScaleLocation,
pBufferDispenser,
&pVBB
));
*ppVertexBufferBuilder = pVBB;
}
else if (!(vfOut & ~CHwTVertexBuffer<CD3DVertexXYZDUV8>::Builder::GetOutVertexFormat()))
{
CHwTVertexBuffer<CD3DVertexXYZDUV8> *pVB = pDevice->GetVB_XYZRHWDUV8();
CHwTVertexBuffer<CD3DVertexXYZDUV8>::Builder *pVBB = NULL;
IFC(CHwTVertexBuffer<CD3DVertexXYZDUV8>::Builder::Create(
pVB,
vfIn,
vfOut,
mvfaAntiAliasScaleLocation,
pBufferDispenser,
&pVBB
));
*ppVertexBufferBuilder = pVBB;
}
else
{
// NOTE-2004/03/22-chrisra Adding another vertexbuffer type requires updating enum
//
// If we add another buffer builder type kMaxVertexBuilderSize enum in hwvertexbuffer.h file
// needs to be updated to reflect possible changes to the maximum size of buffer builders.
//
IFC(E_NOTIMPL);
}
// Store the pipeline, if any, which this VBB can use to spill the vertex buffer to if it
// overflows.
(**ppVertexBufferBuilder).m_pPipelineNoRef = pPipeline;
(**ppVertexBufferBuilder).m_pDeviceNoRef = pDevice;
Cleanup:
RRETURN(hr);*/
//hr
}
/*fn AreWafffling(&self) -> bool {
false
}*/
// Helpful m_rcOutsideBounds casts.
fn OutsideLeft(&self) -> f32 { return self.m_rcOutsideBounds.left as f32; }
fn OutsideRight(&self) -> f32 { return self.m_rcOutsideBounds.right as f32; }
fn OutsideTop(&self) -> f32 { return self.m_rcOutsideBounds.top as f32; }
fn OutsideBottom(&self) -> f32 { return self.m_rcOutsideBounds.bottom as f32; }
}
//+----------------------------------------------------------------------------
//
// Class: THwTVertexMappings<class TVertex>
//
//-----------------------------------------------------------------------------
//+----------------------------------------------------------------------------
//
// Member: THwTVertexMappings<TVertex>::THwTVertexMappings
//
// Synopsis: ctor
//
//-----------------------------------------------------------------------------
/*
template <class TVertex>
CHwTVertexMappings<TVertex>::CHwTVertexMappings()
:
m_mvfMapped(MILVFAttrNone)
{
for (int i = 0; i < ARRAY_SIZE(m_rgWaffleMode); ++i)
{
m_rgWaffleMode[i] = WaffleModeNone;
}
m_matPos2DTransform.SetIdentity();
}
//+----------------------------------------------------------------------------
//
// Member: THwTVertexMappings<TVertex>::SetPositionTransform
//
// Synopsis: Sets the position transform that needs to be applied.
//
//-----------------------------------------------------------------------------
template <class TVertex>
void
CHwTVertexMappings<TVertex>::SetPositionTransform(
__in_ecount(1) const MILMatrix3x2 &matPositionTransform
)
{
m_matPos2DTransform = matPositionTransform;
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexMappings<TVertex>::SetConstantMapping
//
// Synopsis: Remember the static color for the given vertex field
//
template <class TVertex>
HRESULT
CHwTVertexMappings<TVertex>::SetConstantMapping(
MilVertexFormatAttribute mvfaLocation,
__in_ecount(1) const CHwConstantColorSource *pConstCS
)
{
HRESULT hr = S_OK;
Assert(!(m_mvfMapped & mvfaLocation));
pConstCS->GetColor(m_colorStatic);
m_mvfMapped |= mvfaLocation; // Remember this field has been mapped
RRETURN(hr);
}
//+----------------------------------------------------------------------------
//
// Function: GetMILVFAttributeOfTextureCoord
//
// Synopsis: Compute MilVertexFormatAttribute for a texture coordinate index
//
MIL_FORCEINLINE
MilVertexFormat
GetMILVFAttributeOfTextureCoord(
DWORD dwCoordIndex
)
{
return MILVFAttrUV1 << dwCoordIndex;
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexMappings<TVertex>::PointToUV
//
// Synopsis: Helper function to populate the texture coordinates at the given
// index using the given point
//
template <class TVertex>
MIL_FORCEINLINE void
CHwTVertexMappings<TVertex>::PointToUV(
__in_ecount(1) const MilPoint2F &ptIn,
__bound UINT uIndex,
__out_ecount(1) TVertex *pvOut
)
{
m_rgmatPointToUV[uIndex].TransformPoint(
&pvOut->ptTx[uIndex],
ptIn.X,
ptIn.Y
);
}
//+----------------------------------------------------------------------------
//
// Class: CHwTVertexBuffer<TVertex>::Builder
//
//-----------------------------------------------------------------------------
*/
impl<'y, 'z, TVertex: Default> CHwTVertexBufferBuilder<'y, 'z, TVertex> {
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::Create
//
// Synopsis: Instantiate a specific type of vertex builder
//
fn CreateTemplate(
pVertexBuffer: &'y mut CHwTVertexBuffer<'z, TVertex>,
mvfIn: MilVertexFormat,
mvfOut: MilVertexFormat,
mvfaAntiAliasScaleLocation: MilVertexFormatAttribute,
/*pBufferDispenser: __inout_ecount(1) CBufferDispenser *,*/
) -> Self
{
let mut pVertexBufferBuilder = CHwTVertexBufferBuilder::<TVertex>::new(pVertexBuffer);
IFC!(pVertexBufferBuilder.SetupConverter(
mvfIn,
mvfOut,
mvfaAntiAliasScaleLocation
));
return pVertexBufferBuilder;
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::Builder
//
// Synopsis: ctor
//
//-----------------------------------------------------------------------------
fn new(pVertexBuffer: &'y mut CHwTVertexBuffer<'z, TVertex>) -> Self
{
Self {
m_pVB: pVertexBuffer,
//m_rgoPrecomputedTriListVertices: NULL(),
//m_cPrecomputedTriListVertices: 0,
//m_rguPrecomputedTriListIndices: NULL(),
//m_cPrecomputedTriListIndices: 0,
// These two track the Y extent of the shape this builder is producing.
m_rCurStratumTop: f32::MAX,
m_rCurStratumBottom: -f32::MAX,
m_fNeedOutsideGeometry: false,
m_fNeedInsideGeometry: true,
m_rLastTrapezoidRight: -f32::MAX,
m_rLastTrapezoidTopRight: -f32::MAX,
m_rLastTrapezoidBottomRight: -f32::MAX,
m_fHasFlushed: false,
//m_map: Default::default(),
m_rcOutsideBounds: Default::default(),
#[cfg(debug_assertions)]
m_mvfDbgOut: MilVertexFormatAttribute::MILVFAttrNone as MilVertexFormat,
m_mvfIn: MilVertexFormatAttribute::MILVFAttrNone as MilVertexFormat,
m_mvfGenerated: MilVertexFormatAttribute::MILVFAttrNone as MilVertexFormat,
m_mvfaAntiAliasScaleLocation: MilVertexFormatAttribute::MILVFAttrNone,
}
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::SetupConverter
//
// Synopsis: Choose the appropriate conversion method
//
fn SetupConverter(&mut self,
mvfIn: MilVertexFormat,
mvfOut: MilVertexFormat,
mvfaAntiAliasScaleLocation: MilVertexFormatAttribute,
) -> HRESULT
{
let hr = S_OK;
self.m_mvfIn = mvfIn;
#[cfg(debug_assertions)]
{
self.m_mvfDbgOut = mvfOut;
}
self.m_mvfGenerated = mvfOut & !self.m_mvfIn;
self.m_mvfaAntiAliasScaleLocation = mvfaAntiAliasScaleLocation;
assert!((self.m_mvfGenerated & MilVertexFormatAttribute::MILVFAttrXY as MilVertexFormat) == 0);
RRETURN!(hr);
}
}
/*
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::SetTransformMapping
//
// Synopsis: Delegate mapping sets to CHwTVertexMappings
//
//-----------------------------------------------------------------------------
template <class TVertex>
void
CHwTVertexBuffer<TVertex>::Builder::SetTransformMapping(
__in_ecount(1) const MILMatrix3x2 &mat2DPositionTransform
)
{
m_map.SetPositionTransform(mat2DPositionTransform);
}
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::Builder::SetConstantMapping(
MilVertexFormatAttribute mvfaLocation,
__in_ecount(1) const CHwConstantColorSource *pConstCS
)
{
HRESULT hr = S_OK;
IFC(m_map.SetConstantMapping(mvfaLocation, pConstCS));
Cleanup:
RRETURN(hr);
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::FinalizeMappings
//
// Synopsis: Complete setup of vertex mappings
//
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::Builder::FinalizeMappings(
)
{
HRESULT hr = S_OK;
//
// Set default Z if required.
//
if (m_mvfGenerated & MILVFAttrZ)
{
if (!(m_map.m_mvfMapped & MILVFAttrZ))
{
m_map.m_vStatic.Z = 0.5f;
}
}
//
// If AA falloff is not going to scale the diffuse color and it is
// generated then see if the color is constant such that we can do any
// complex conversions just once here instead of in every iteration of the
// expansion loop. If AA falloff is going to scale the diffuse color then
// we can still optimize for the falloff = 1.0 case by precomputing that
// color now and checking for 1.0 during generation. Such a precomputation
// has shown significant to performance.
//
if (m_mvfGenerated & MILVFAttrDiffuse)
{
if (m_map.m_mvfMapped & MILVFAttrDiffuse)
{
// Assumes diffuse color is constant
m_map.m_vStatic.Diffuse =
Convert_MilColorF_scRGB_To_Premultiplied_MilColorB_sRGB(&m_map.m_colorStatic);
}
else
{
// Set default Diffuse value: White
m_map.m_vStatic.Diffuse = MIL_COLOR(0xFF,0xFF,0xFF,0xFF);
}
}
RRETURN(hr);
}*/
impl<TVertex> CHwTVertexBufferBuilder<'_, '_, TVertex> {
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::SetOutsideBounds
//
//
// Synopsis: Enables rendering geometry for areas outside the shape but
// within the bounds. These areas will be created with
// zero alpha.
//
pub fn SetOutsideBounds(&mut self,
prcOutsideBounds: Option<&CMILSurfaceRect>,
fNeedInside: bool,
)
{
// Waffling and outside bounds is not currently implemented. It's
// not difficult to do but currently there is no need.
//assert!(!(self.AreWaffling() && self.prcOutsideBounds));
if let Some(prcOutsideBounds) = prcOutsideBounds
{
self.m_rcOutsideBounds = prcOutsideBounds.clone();
self.m_fNeedOutsideGeometry = true;
self.m_fNeedInsideGeometry = fNeedInside;
}
else
{
self.m_fNeedOutsideGeometry = false;
self.m_fNeedInsideGeometry = true;
}
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::BeginBuilding
//
// Synopsis: Prepare for a new primitive by resetting the vertex buffer
//
pub fn BeginBuilding(&mut self,
) -> HRESULT
{
let hr: HRESULT = S_OK;
self.m_fHasFlushed = false;
self.m_pVB.Reset(/*self*/);
RRETURN!(hr);
}
}
impl IGeometrySink for CHwVertexBufferBuilder<'_, '_> {
fn AddTrapezoid(&mut self,
rPixelYTop: f32, // In: y coordinate of top of trapezoid
rPixelXTopLeft: f32, // In: x coordinate for top left
rPixelXTopRight: f32, // In: x coordinate for top right
rPixelYBottom: f32, // In: y coordinate of bottom of trapezoid
rPixelXBottomLeft: f32, // In: x coordinate for bottom left
rPixelXBottomRight: f32, // In: x coordinate for bottom right
rPixelXLeftDelta: f32, // In: trapezoid expand radius for left edge
rPixelXRightDelta: f32 // In: trapezoid expand radius for right edge
) -> HRESULT
{
let hr = S_OK;
if (/*self.AreWaffling()*/ false)
{
/*IFC(AddTrapezoidWaffle(
rPixelYTop,
rPixelXTopLeft,
rPixelXTopRight,
rPixelYBottom,
rPixelXBottomLeft,
rPixelXBottomRight,
rPixelXLeftDelta,
rPixelXRightDelta));*/
}
else
{
IFC!(self.AddTrapezoidStandard(
rPixelYTop,
rPixelXTopLeft,
rPixelXTopRight,
rPixelYBottom,
rPixelXBottomLeft,
rPixelXBottomRight,
rPixelXLeftDelta,
rPixelXRightDelta));
}
//Cleanup:
RRETURN!(hr);
}
fn IsEmpty(&self) -> bool {
self.m_pVB.IsEmpty()
}
/*
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddVertex
//
// Synopsis: Add a vertex to the vertex buffer
//
// Remember just the given vertex information now and convert later
// in a single, more optimal pass.
//
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::Builder::AddVertex(
__in_ecount(1) const MilPoint2F &ptPosition,
// Vertex coordinates
__out_ecount(1) WORD *pIndex
// The index of the new vertex
)
{
HRESULT hr = S_OK;
Assert(!NeedOutsideGeometry());
Assert(m_mvfIn == MILVFAttrXY);
TVertex *pVertex;
IFC(m_pVB->AddTriListVertices(1, &pVertex, pIndex));
pVertex->ptPt = ptPosition;
// store coverage as a DWORD instead of float
pVertex->Diffuse = FLOAT_ONE;
Cleanup:
RRETURN(hr);
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddIndexedVertices, IGeometrySink
//
// Synopsis: Add a fully computed, indexed vertex to the vertex buffer
//
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::Builder::AddIndexedVertices(
UINT cVertices,
// In: number of vertices
__in_bcount(cVertices*uVertexStride) const void *pVertexBufferNoRef,
// In: vertex buffer containing the vertices
UINT uVertexStride,
// In: size of each vertex
MilVertexFormat mvfFormat,
// In: format of each vertex
UINT cIndices,
// In: Number of indices
__in_ecount(cIndices) const UINT *puIndexBuffer
// In: index buffer
)
{
Assert(m_mvfIn & (MILVFAttrXYZ | MILVFAttrDiffuse | MILVFAttrUV2));
Assert(mvfFormat == (MILVFAttrXYZ | MILVFAttrDiffuse | MILVFAttrUV2));
Assert(uVertexStride == sizeof(TVertex));
m_rgoPrecomputedTriListVertices = reinterpret_cast<const TVertex *>(pVertexBufferNoRef);
m_cPrecomputedTriListVertices = cVertices;
m_rguPrecomputedTriListIndices = puIndexBuffer;
m_cPrecomputedTriListIndices = cIndices;
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddTriangle
//
// Synopsis: Add a triangle to the vertex buffer
//
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::Builder::AddTriangle(
DWORD i1, // In: Index of triangle's first vertex
DWORD i2, // In: Index of triangle's second vertex
DWORD i3 // In: Index of triangle's third vertex
)
{
HRESULT hr = S_OK;
Assert(!NeedOutsideGeometry());
if (AreWaffling())
{
TVertex *pVertex;
UINT uNumVertices;
m_pVB->GetTriListVertices(&pVertex, &uNumVertices);
Assert(i1 < uNumVertices);
Assert(i2 < uNumVertices);
Assert(i3 < uNumVertices);
PointXYA rgPoints[3];
rgPoints[0].x = pVertex[i1].ptPt.X;
rgPoints[0].y = pVertex[i1].ptPt.Y;
rgPoints[0].a = 1;
rgPoints[1].x = pVertex[i2].ptPt.X;
rgPoints[1].y = pVertex[i2].ptPt.Y;
rgPoints[1].a = 1;
rgPoints[2].x = pVertex[i3].ptPt.X;
rgPoints[2].y = pVertex[i3].ptPt.Y;
rgPoints[2].a = 1;
TriangleWaffler<PointXYA> wafflers[NUM_OF_VERTEX_TEXTURE_COORDS(TVertex) * 2];
TriangleWaffler<PointXYA>::ISink *pWaffleSinkNoRef = BuildWafflePipeline(wafflers);
IFC(pWaffleSinkNoRef->AddTriangle(rgPoints[0], rgPoints[1], rgPoints[2]));
}
else
{
IFC(m_pVB->AddTriangle(
static_cast<WORD>(i1),
static_cast<WORD>(i2),
static_cast<WORD>(i3)
));
}
Cleanup:
RRETURN(hr);
}
*/
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddComplexScan
//
// Synopsis: Add a coverage span to the vertex buffer
//
//-----------------------------------------------------------------------------
fn AddComplexScan(&mut self,
nPixelY: INT,
// In: y coordinate in pixel space
mut pIntervalSpanStart: Ref<crate::aacoverage::CCoverageInterval>
// In: coverage segments
) -> HRESULT {
let hr: HRESULT = S_OK;
//let pVertex: *mut CD3DVertexXYZDUV2 = NULL();
IFC!(self.PrepareStratum((nPixelY) as f32,
(nPixelY+1) as f32,
false, /* Not a trapezoid. */
0., 0.,
0., 0., 0., 0.));
let rPixelY: f32;
rPixelY = (nPixelY) as f32 + 0.5;
//LineWaffler<PointXYA> wafflers[NUM_OF_VERTEX_TEXTURE_COORDS(TVertex) * 2];
// Use sink for waffling & the first line fix up (aka the complicated cases.)
//ILineSink<PointXYA> *pLineSink = NULL;
let mut pLineSink = None;
/*if (self.AreWaffling())
{
bool fWafflersUsed;
pLineSink = BuildWafflePipeline(wafflers, OUT fWafflersUsed);
if (!fWafflersUsed)
{
pLineSink = NULL;
}
}*/
// Use triangles instead of lines, for lines too close to the top of the viewport
// because lines are clipped (before rasterization) against a viewport that only
// includes half of the top pixel row. Waffling will take care of this separately.
if (/*pLineSink.is_none() && rPixelY < self.GetViewportTop() + 1 ||*/ FORCE_TRIANGLES)
{
pLineSink = Some(&mut self.m_pVB);
}
//
// Output all segments if creating outside geometry, otherwise only output segments
// with non-zero coverage.
//
if (pLineSink.is_none())
{
/*
UINT nSegmentCount = 0;
for (const CCoverageInterval *pIntervalSpanTemp = pIntervalSpanStart;
pIntervalSpanTemp->m_nPixelX != INT_MAX;
pIntervalSpanTemp = pIntervalSpanTemp->m_pNext
)
{
if (NeedCoverageGeometry(pIntervalSpanTemp->m_nCoverage))
{
++nSegmentCount;
}
}
//
// Add vertices
//
if (nSegmentCount)
{
IFC(m_pVB->AddLineListVertices(nSegmentCount*2, &pVertex));
}*/
}
//
// Having allocated space (if not using sink), now let's actually output the vertices.
//
while ((*pIntervalSpanStart).m_nPixelX.get() != INT::MAX)
{
assert!(!(*pIntervalSpanStart).m_pNext.get().is_null());
//
// Output line list segments
//
// Note that line segments light pixels by going through the the
// "diamond" interior of a pixel. While we could accomplish this
// by going from left edge to right edge of pixel, D3D10 uses the
// convention that the LASTPIXEL is never lit. We respect that now
// by setting D3DRS_LASTPIXEL to FALSE and use line segments that
// start in center of first pixel and end in center of one pixel
// beyond last.
//
// Since our top left corner is integer, we add 0.5 to get to the
// pixel center.
//
if (self.NeedCoverageGeometry((*pIntervalSpanStart).m_nCoverage.get()))
{
let rCoverage: f32 = ((*pIntervalSpanStart).m_nCoverage.get() as f32)/(c_nShiftSizeSquared as f32);
let mut iBegin: LONG = (*pIntervalSpanStart).m_nPixelX.get();
let mut iEnd: LONG = (*(*pIntervalSpanStart).m_pNext.get()).m_nPixelX.get();
if (self.NeedOutsideGeometry())
{
// Intersect the interval with the outside bounds to create
// start and stop lines. The scan begins (ends) with an
// interval starting (ending) at -inf (+inf).
// The given geometry is not guaranteed to be within m_rcOutsideBounds but
// the additional inner min and max (in that order) produce empty spans
// for intervals not intersecting m_rcOutsideBounds.
//
// We could cull here but that should really be done by the geometry
// generator.
iBegin = iBegin.max(iEnd.min(self.m_rcOutsideBounds.left));
iEnd = iEnd.min(iBegin.max(self.m_rcOutsideBounds.right));
}
let rPixelXBegin: f32= (iBegin as f32) + 0.5;
let rPixelXEnd: f32 = (iEnd as f32) + 0.5;
//
// Output line (linelist or tristrip) for a pixel
//
//if let Some(pLineSink) = pLineSink
{
let mut v0: PointXYA = Default::default(); let mut v1: PointXYA = Default::default();
v0.x = rPixelXBegin;
v0.y = rPixelY;
v0.a = rCoverage;
v1.x = rPixelXEnd;
v1.y = rPixelY;
v1.a = rCoverage;
IFC!(self.m_pVB.AddLine(&v0,&v1));
}
//else
{
/*
let dwDiffuse = ReinterpretFloatAsDWORD(rCoverage);
pVertex[0].ptPt.X = rPixelXBegin;
pVertex[0].ptPt.Y = rPixelY;
pVertex[0].Diffuse = dwDiffuse;
pVertex[1].ptPt.X = rPixelXEnd;
pVertex[1].ptPt.Y = rPixelY;
pVertex[1].Diffuse = dwDiffuse;
// Advance output vertex pointer
pVertex += 2;*/
}
}
//
// Advance coverage buffer
//
pIntervalSpanStart = (*pIntervalSpanStart).m_pNext.get();
}
//Cleanup:
RRETURN!(hr);
}
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddLineAsTriangleList
//
// Synopsis: Adds a horizontal line as a triangle list to work around
// issue in D3D9 where horizontal lines with y = 0 may not render.
//
// Line clipping in D3D9
// This behavior will change in D3D10 and this work-around will no
// longer be needed. (Pixel center conventions will also change.)
//
//-----------------------------------------------------------------------------
impl CHwVertexBuffer<'_> {
fn AddLineAsTriangleList(&mut self,
pBegin: &CD3DVertexXYZDUV2, // Begin
pEnd: &CD3DVertexXYZDUV2 // End
) -> HRESULT
{
let hr = S_OK;
// Collect pertinent data from vertices.
debug_assert!(pBegin.y == pEnd.y);
debug_assert!(pBegin.coverage == pEnd.coverage);
// Offset begin and end X left by 0.5 because the line starts on the first
// pixel center and ends on the center of the pixel after the line segment.
let x0 = pBegin.x - 0.5;
let x1 = pEnd.x - 0.5;
let y = pBegin.y;
let dwDiffuse = pBegin.coverage;
//
// Add the vertices
//
// OpenGL doesn't specify how vertex positions are converted to fixed point prior to rasterization. On macOS, with AMD GPUs,
// the GPU appears to truncate to fixed point instead of rounding. This behaviour is controlled by PA_SU_VTX_CNTL
// register. To handle this we'll add a 1./512. subpixel bias to the center vertex to cause the coordinates to round instead
// of truncate.
//
// D3D11 requires the fixed point integer result to be within 0.6ULP which implicitly disallows the truncate behaviour above.
// This means that D2D doesn't need to deal with this problem.
let subpixel_bias = self.subpixel_bias;
// Use a single triangle to cover the entire line
self.AddTriVertices(
OutputVertex{ x: x0, y: y - 0.5, coverage: dwDiffuse },
OutputVertex{ x: x0, y: y + 0.5, coverage: dwDiffuse },
OutputVertex{ x: x1, y: y + subpixel_bias, coverage: dwDiffuse },
);
self.AddedNonLineSegment();
//Cleanup:
RRETURN!(hr);
}
}
/*
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddParallelogram
//
// Synopsis: This function adds the coordinates of a parallelogram to the vertex strip buffer.
//
// Parameter: rgPosition contains four coordinates of the parallelogram. Coordinates should have
// a winding order
//
//-----------------------------------------------------------------------------
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::Builder::AddParallelogram(
__in_ecount(4) const MilPoint2F *rgPosition
)
{
HRESULT hr = S_OK;
if (AreWaffling())
{
PointXYA rgPoints[4];
for (int i = 0; i < 4; ++i)
{
rgPoints[i].x = rgPosition[i].X;
rgPoints[i].y = rgPosition[i].Y;
rgPoints[i].a = 1;
}
TriangleWaffler<PointXYA> wafflers[NUM_OF_VERTEX_TEXTURE_COORDS(TVertex) * 2];
TriangleWaffler<PointXYA>::ISink *pWaffleSinkNoRef = BuildWafflePipeline(wafflers);
IFC(pWaffleSinkNoRef->AddTriangle(rgPoints[0], rgPoints[1], rgPoints[3]));
IFC(pWaffleSinkNoRef->AddTriangle(rgPoints[3], rgPoints[1], rgPoints[2]));
}
else
{
TVertex *pVertex;
//
// Add the vertices
//
IFC(m_pVB->AddTriStripVertices(6, &pVertex));
//
// Duplicate the first vertex. This creates 2 degenerate triangles: one connecting
// the previous rect to this one and another between vertices 0 and 1.
//
pVertex[0].ptPt = rgPosition[0];
pVertex[0].Diffuse = FLOAT_ONE;
pVertex[1].ptPt = rgPosition[0];
pVertex[1].Diffuse = FLOAT_ONE;
pVertex[2].ptPt = rgPosition[1];
pVertex[2].Diffuse = FLOAT_ONE;
pVertex[3].ptPt = rgPosition[3];
pVertex[3].Diffuse = FLOAT_ONE;
pVertex[4].ptPt = rgPosition[2];
pVertex[4].Diffuse = FLOAT_ONE;
//
// Duplicate the last vertex. This creates 2 degenerate triangles: one
// between vertices 4 and 5 and one connecting this Rect to the
// next one.
//
pVertex[5].ptPt = rgPosition[2];
pVertex[5].Diffuse = FLOAT_ONE;
}
Cleanup:
RRETURN(hr);
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::BuildWafflePipeline<TWaffler>
//
// Synopsis: Builds a pipeline of wafflers into the provided array of wafflers.
// And returns a pointer (not to be deleted) to the input sink
// of the waffle pipeline.
// the final result is sinked int m_pVB.
//
//-----------------------------------------------------------------------------
template<class TVertex>
template<class TWaffler>
__out_ecount(1) typename TWaffler::ISink *
CHwTVertexBuffer<TVertex>::Builder::BuildWafflePipeline(
__out_xcount(NUM_OF_VERTEX_TEXTURE_COORDS(TVertex) * 2) TWaffler *wafflers,
__out_ecount(1) bool &fWafflersUsed
) const
{
UINT count = 0;
for (int i = 0; i < NUM_OF_VERTEX_TEXTURE_COORDS(TVertex); ++i)
{
if (m_map.m_rgWaffleMode[i] != 0)
{
const MILMatrix3x2 &pMatWaffle = m_map.m_rgmatPointToUV[i];
// Each column ([a,b,c] transpose) of this matrix specifies a waffler that
// partitions the plane into regions between the lines:
// ax + by + c = k
// for every integer k.
//
// If this partition width is substantially less than a pixel we have
// serious problems with waffling generating too many triangles for
// doubtful visual effect so we don't perform a waffling with width less
// than c_rMinWaffleWidthPixels. So we need to know the width of the partition
// regions:
//
// Changing c just translates the partition so let's assume c = 0.
// The line ax + by = 0 goes through the origin and the line ax + by
// = 1 is adjacent to it in the partition. The distance between
// these lines is also the distance from ax + by = 1 to the origin.
// Using Lagrange multipliers we can determine that this distance
// is
// 1/sqrt(a*a+b*b).
// We want to avoid waffling if this is less than c_rMinWaffleWidthPixels
// or equivalently:
// 1/sqrt(a*a+b*b) < c_rMinWaffleWidthPixels
// sqrt(a*a+b*b) > 1/c_rMinWaffleWidthPixels
// a*a+b*b > 1/(c_rMinWaffleWidthPixels*c_rMinWaffleWidthPixels)
//
const float c_rMaxWaffleMagnitude = 1/(c_rMinWaffleWidthPixels*c_rMinWaffleWidthPixels);
float mag0 = pMatWaffle.m_00*pMatWaffle.m_00+pMatWaffle.m_10*pMatWaffle.m_10;
if (mag0 < c_rMaxWaffleMagnitude)
{
wafflers[count].Set(pMatWaffle.m_00, pMatWaffle.m_10, pMatWaffle.m_20, wafflers+count+1);
++count;
}
float mag1 = pMatWaffle.m_01*pMatWaffle.m_01+pMatWaffle.m_11*pMatWaffle.m_11;
if (mag1 < c_rMaxWaffleMagnitude)
{
wafflers[count].Set(pMatWaffle.m_01, pMatWaffle.m_11, pMatWaffle.m_21, wafflers+count+1);
++count;
}
}
}
if (count)
{
fWafflersUsed = true;
// As the last step in the chain we send the triangles to our vertex buffer.
wafflers[count-1].SetSink(m_pVB);
return &wafflers[0];
}
else
{
fWafflersUsed = false;
// If we built no wafflers then sink straight into the vertex buffer.
return m_pVB;
}
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::IsEmpty
//
// Synopsis: Does our VB have any triangles/lines?
//
//-----------------------------------------------------------------------------
template <class TVertex>
BOOL
CHwTVertexBuffer<TVertex>::Builder::IsEmpty()
{
return m_pVB->IsEmpty();
}
*/
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddTrapezoid
//
// Synopsis: Add a trapezoid to the vertex buffer
//
//
// left edge right edge
// ___+_________________+___ <<< top edge
// / + / \ + \
// / + / \ + \
// / + / \ + \
// /__+__/___________________\__+__\ <<< bottom edge
// + ^^ +
// delta
//
impl CHwVertexBufferBuilder<'_, '_> {
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddTrapezoidStandard
//
// Synopsis: See AddTrapezoid. This doesn't do waffling & uses tri strips.
//
fn AddTrapezoidStandard(&mut self,
rPixelYTop: f32, // In: y coordinate of top of trapezoid
rPixelXTopLeft: f32, // In: x coordinate for top left
rPixelXTopRight: f32, // In: x coordinate for top right
rPixelYBottom: f32, // In: y coordinate of bottom of trapezoid
rPixelXBottomLeft: f32, // In: x coordinate for bottom left
rPixelXBottomRight: f32, // In: x coordinate for bottom right
rPixelXLeftDelta: f32, // In: trapezoid expand radius for left edge
rPixelXRightDelta: f32 // In: trapezoid expand radius for right edge
) -> HRESULT
{
type TVertex = CD3DVertexXYZDUV2;
let hr = S_OK;
//TVertex *pVertex;
IFC!(self.PrepareStratum(
rPixelYTop,
rPixelYBottom,
true, /* Trapezoid */
rPixelXTopLeft.min(rPixelXBottomLeft),
rPixelXTopRight.max(rPixelXBottomRight),
rPixelXTopLeft - rPixelXLeftDelta, rPixelXBottomLeft - rPixelXLeftDelta,
rPixelXTopRight + rPixelXRightDelta, rPixelXBottomRight + rPixelXRightDelta
));
//
// Add the vertices
//
let fNeedOutsideGeometry: bool; let fNeedInsideGeometry: bool;
fNeedOutsideGeometry = self.NeedOutsideGeometry();
fNeedInsideGeometry = self.NeedInsideGeometry();
//
// Fill in the vertices
//
self.m_pVB.AddTrapezoidVertices(
OutputVertex{
x: rPixelXTopLeft - rPixelXLeftDelta,
y: rPixelYTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rPixelXBottomLeft - rPixelXLeftDelta,
y: rPixelYBottom,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rPixelXTopLeft + rPixelXLeftDelta,
y: rPixelYTop,
coverage: FLOAT_ONE,
},
OutputVertex{
x: rPixelXBottomLeft + rPixelXLeftDelta,
y: rPixelYBottom,
coverage: FLOAT_ONE,
}
);
if (fNeedInsideGeometry)
{
self.m_pVB.AddTrapezoidVertices(
OutputVertex{
x: rPixelXTopLeft + rPixelXLeftDelta,
y: rPixelYTop,
coverage: FLOAT_ONE,
},
OutputVertex{
x: rPixelXBottomLeft + rPixelXLeftDelta,
y: rPixelYBottom,
coverage: FLOAT_ONE,
},
OutputVertex{
x: rPixelXTopRight - rPixelXRightDelta,
y: rPixelYTop,
coverage: FLOAT_ONE,
},
OutputVertex{
x: rPixelXBottomRight - rPixelXRightDelta,
y: rPixelYBottom,
coverage: FLOAT_ONE,
}
);
}
self.m_pVB.AddTrapezoidVertices(
OutputVertex{
x: rPixelXTopRight - rPixelXRightDelta,
y: rPixelYTop,
coverage: FLOAT_ONE,
},
OutputVertex{
x: rPixelXBottomRight - rPixelXRightDelta,
y: rPixelYBottom,
coverage: FLOAT_ONE,
},
OutputVertex{
x: rPixelXTopRight + rPixelXRightDelta,
y: rPixelYTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rPixelXBottomRight + rPixelXRightDelta,
y: rPixelYBottom,
coverage: FLOAT_ZERO,
}
);
if (!fNeedOutsideGeometry)
{
//
// Duplicate the last vertex. This creates 2 degenerate triangles: one
// between vertices 8 and 9 and one connecting this trapezoid to the
// next one.
//
//pVertex.push(OutputVertex{
// x: rPixelXBottomRight + rPixelXRightDelta,
// y: rPixelYBottom,
// coverage: FLOAT_ZERO,
//});
}
self.m_pVB.AddedNonLineSegment();
//Cleanup:
RRETURN!(hr);
}
}
/*
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::AddTrapezoidWaffle
//
// Synopsis: See AddTrapezoid. This adds a waffled trapezoid.
//
//-----------------------------------------------------------------------------
template <class TVertex>
HRESULT
CHwTVertexBuffer<TVertex>::Builder::AddTrapezoidWaffle(
float rPixelYTop, // In: y coordinate of top of trapezoid
float rPixelXTopLeft, // In: x coordinate for top left
float rPixelXTopRight, // In: x coordinate for top right
float rPixelYBottom, // In: y coordinate of bottom of trapezoid
float rPixelXBottomLeft, // In: x coordinate for bottom left
float rPixelXBottomRight, // In: x coordinate for bottom right
float rPixelXLeftDelta, // In: trapezoid expand radius for left edge
float rPixelXRightDelta // In: trapezoid expand radius for right edge
)
{
HRESULT hr = S_OK;
// We have 2 (u & v) wafflers per texture coordinate that need waffling.
TriangleWaffler<PointXYA> wafflers[NUM_OF_VERTEX_TEXTURE_COORDS(TVertex) * 2];
bool fWafflersUsed = false;
TriangleWaffler<PointXYA>::ISink *pWaffleSinkNoRef = BuildWafflePipeline(wafflers, OUT fWafflersUsed);
PointXYA vertices[8];
//
// Fill in the strip vertices
//
// Nonstandard coverage mapping and waffling are not supported at the same time.
Assert(!NeedOutsideGeometry());
vertices[0].x = rPixelXTopLeft - rPixelXLeftDelta;
vertices[0].y = rPixelYTop;
vertices[0].a = 0;
vertices[1].x = rPixelXBottomLeft - rPixelXLeftDelta;
vertices[1].y = rPixelYBottom;
vertices[1].a = 0;
vertices[2].x = rPixelXTopLeft + rPixelXLeftDelta;
vertices[2].y = rPixelYTop;
vertices[2].a = 1;
vertices[3].x = rPixelXBottomLeft + rPixelXLeftDelta;
vertices[3].y = rPixelYBottom;
vertices[3].a = 1;
vertices[4].x = rPixelXTopRight - rPixelXRightDelta;
vertices[4].y = rPixelYTop;
vertices[4].a = 1;
vertices[5].x = rPixelXBottomRight - rPixelXRightDelta;
vertices[5].y = rPixelYBottom;
vertices[5].a = 1;
vertices[6].x = rPixelXTopRight + rPixelXRightDelta;
vertices[6].y = rPixelYTop;
vertices[6].a = 0;
vertices[7].x = rPixelXBottomRight + rPixelXRightDelta;
vertices[7].y = rPixelYBottom;
vertices[7].a = 0;
// Send the triangles in the strip through the waffle pipeline.
for (int i = 0; i < 6; ++i)
{
IFC(pWaffleSinkNoRef->AddTriangle(vertices[i+1], vertices[i], vertices[i+2]));
}
Cleanup:
RRETURN(hr);
}
*/
impl CHwVertexBufferBuilder<'_, '_> {
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::NeedCoverageGeometry
//
// Synopsis: Returns true if the coverage value needs to be rendered
// based on NeedInsideGeometry() and NeedOutsideGeometry()
//
// Two cases where we don't need to generate geometry:
// 1. NeedInsideGeometry is false, and coverage is c_nShiftSizeSquared.
// 2. NeedOutsideGeometry is false and coverage is 0
//
//-----------------------------------------------------------------------------
fn NeedCoverageGeometry(&self,
nCoverage: INT
) -> bool
{
return (self.NeedInsideGeometry() || nCoverage != c_nShiftSizeSquared)
&& (self.NeedOutsideGeometry() || nCoverage != 0);
}
//+------------------------------------------------------------------------
//
// Member: NeedOutsideGeometry
//
// Synopsis: True if we should create geometry with zero alpha for
// areas outside the input geometry but within a given
// bounding box.
//
//-------------------------------------------------------------------------
fn NeedOutsideGeometry(&self) -> bool
{
return self.m_fNeedOutsideGeometry;
}
//+------------------------------------------------------------------------
//
// Member: NeedInsideGeometry
//
// Synopsis: True if we should create geometry for areas completely
// withing the input geometry (i.e. alpha 1.) Should only
// be false if NeedOutsideGeometry is true.
//
//-------------------------------------------------------------------------
fn NeedInsideGeometry(&self) -> bool
{
assert!(self.m_fNeedOutsideGeometry || self.m_fNeedInsideGeometry);
return self.m_fNeedInsideGeometry;
}
// Helpers that handle extra shapes in trapezoid mode.
fn PrepareStratum(&mut self,
rStratumTop: f32,
rStratumBottom: f32,
fTrapezoid: bool,
rTrapezoidLeft: f32,
rTrapezoidRight: f32,
rTrapezoidTopLeft: f32, // = 0
rTrapezoidBottomLeft: f32, // = 0
rTrapezoidTopRight: f32, // = 0
rTrapezoidBottomRight: f32, // = 0
) -> HRESULT
{
return if self.NeedOutsideGeometry() {
self.PrepareStratumSlow(
rStratumTop,
rStratumBottom,
fTrapezoid,
rTrapezoidLeft,
rTrapezoidRight,
rTrapezoidTopLeft,
rTrapezoidBottomLeft,
rTrapezoidTopRight,
rTrapezoidBottomRight
)
} else { S_OK };
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::PrepareStratumSlow
//
// Synopsis: Call before producing a new stratum (complex span or trapezoid.)
// Handles several tasks:
// 1. Producing between top of complement geometry & the 1st
// stratum or when a gap between strata occurs (because
// the geometry is not closed and has horizontal gaps.)
// Passing in FLT_MAX for rStratumTop and rStratumBottom
// Fills the gap between the last stratum and the bottom
// of the outside.
// 2. Begins and/or ends the triangle strip corresponding to
// a trapezoid row.
// 3. Updates status vars m_rCurStratumTop & m_rCurStratumBottom
//
// Note: Call PrepareStratum which inlines the check for NeedOutsideGeometry()
// If NeedOutsideGeometry is false PrepareStratum() does nothing.
// This (slow) version asserts NeedOutsideGeometry()
//
//-----------------------------------------------------------------------------
fn PrepareStratumSlow(&mut self,
rStratumTop: f32,
rStratumBottom: f32,
fTrapezoid: bool,
rTrapezoidLeft: f32,
rTrapezoidRight: f32,
rTrapezoidTopLeft: f32,
rTrapezoidBottomLeft: f32,
rTrapezoidTopRight: f32,
rTrapezoidBottomRight: f32,
) -> HRESULT
{
type TVertex = OutputVertex;
let hr: HRESULT = S_OK;
assert!(!(rStratumTop > rStratumBottom));
assert!(self.NeedOutsideGeometry());
// There's only once case where a stratum can go "backwards"
// and that's when we're done building & calling from
// EndBuildingOutside
let fEndBuildingOutside: f32 = (rStratumBottom == self.OutsideBottom() &&
rStratumTop == self.OutsideBottom()) as i32 as f32;
if (fEndBuildingOutside == 1.)
{
assert!(!fTrapezoid);
}
else
{
assert!(!(rStratumBottom < self.m_rCurStratumBottom));
}
if ( fEndBuildingOutside == 1.
|| rStratumBottom != self.m_rCurStratumBottom)
{
// New stratum starting now. Two things to do
// 1. Close out current trapezoid stratum if necessary.
// 2. Begin new trapezoid stratum if necessary.
if (self.m_rCurStratumTop != f32::MAX)
{
// we do not clip trapezoids so RIGHT boundary
// of the stratus can be outside of m_rcOutsideBounds.
let rOutsideRight: f32 = self.OutsideRight().max(self.m_rLastTrapezoidRight);
// End current trapezoid stratum.
self.m_pVB.AddTrapezoidVertices(
OutputVertex{
x: self.m_rLastTrapezoidTopRight,
y: self.m_rCurStratumTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: self.m_rLastTrapezoidBottomRight,
y: self.m_rCurStratumBottom,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rOutsideRight,
y: self.m_rCurStratumTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rOutsideRight,
y: self.m_rCurStratumBottom,
coverage: FLOAT_ZERO,
}
);
}
// Compute the gap between where the last stratum ended and where
// this one begins.
let flGap: f32 = rStratumTop - self.m_rCurStratumBottom;
if (flGap > 0.)
{
// The "special" case of a gap at the beginning is caught here
// using the sentinel initial value of m_rCurStratumBottom.
let flRectTop: f32 = if self.m_rCurStratumBottom == -f32::MAX {
self.OutsideTop() } else {
self.m_rCurStratumBottom };
let flRectBot: f32 = (rStratumTop as f32);
// Produce rectangular for any horizontal intervals in the
// outside bounds that have no generated geometry.
assert!(self.m_rCurStratumBottom != -f32::MAX || self.m_rCurStratumTop == f32::MAX);
let outside_left = self.OutsideLeft();
let outside_right = self.OutsideRight();
// Duplicate first vertex.
self.m_pVB.AddTrapezoidVertices(
OutputVertex{
x: outside_left,
y: flRectTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: outside_left,
y: flRectBot,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: outside_right,
y: flRectTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: outside_right,
y: flRectBot,
coverage: FLOAT_ZERO,
}
);
}
if (fTrapezoid)
{
// we do not clip trapezoids so left boundary
// of the stratus can be outside of m_rcOutsideBounds.
let rOutsideLeft: f32 = self.OutsideLeft().min(rTrapezoidLeft);
// Begin new trapezoid stratum.
self.m_pVB.AddTrapezoidVertices(
OutputVertex{
x: rOutsideLeft,
y: rStratumTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rOutsideLeft,
y: rStratumBottom,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rTrapezoidTopLeft,
y: rStratumTop,
coverage: FLOAT_ZERO,
},
OutputVertex{
x: rTrapezoidBottomLeft,
y: rStratumBottom,
coverage: FLOAT_ZERO,
}
);
}
}
if (fTrapezoid)
{
self.m_rLastTrapezoidTopRight = rTrapezoidTopRight;
self.m_rLastTrapezoidBottomRight = rTrapezoidBottomRight;
self.m_rLastTrapezoidRight = rTrapezoidRight;
}
self.m_rCurStratumTop = if fTrapezoid { rStratumTop } else { f32::MAX };
self.m_rCurStratumBottom = rStratumBottom;
RRETURN!(hr);
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::EndBuildingOutside
//
// Synopsis: Finish creating outside geometry.
// 1. If no geometry was created then just fill bounds.
// 2. Otherwise:
// A. End last trapezoid row
// B. Produce stop stratum
//
//-----------------------------------------------------------------------------
fn EndBuildingOutside(&mut self) -> HRESULT
{
return self.PrepareStratum(
self.OutsideBottom(),
self.OutsideBottom(),
false, /* Not a trapezoid. */
0., 0.,
0., 0.,
0., 0.,
);
}
//+----------------------------------------------------------------------------
//
// Member: CHwTVertexBuffer<TVertex>::Builder::EndBuilding
//
// Synopsis: Expand all vertices to the full required format and return
// vertex buffer.
//
//-----------------------------------------------------------------------------
pub fn EndBuilding(&mut self) -> HRESULT
{
let hr = S_OK;
IFC!(self.EndBuildingOutside());
//Cleanup:
RRETURN!(hr);
}
}