Related
According to this example from Apple, Tier 1 Argument Buffers cannot be accessed through pointer indexing (https://developer.apple.com/documentation/metal/buffers/about_argument_buffers). If this is not allowed, how can I index into a particular struct in my argument buffer array?
// Shader.metal
struct MyTexture {
metal::texture2d<float, metal::access::sample> texture;
};
fragment half4 myFragment(VertexOut vert [[stage_in]],
....,
constant int &count [[buffer(4)]],
constant MyTexture *textures [[buffer(5)]],
....)
{
for(int i = 0; i < count; i++) {
MyTexture resource = textures[i];
float4 color = resource.texture.sample(sampler, pos.xy);
outputColor = mix(inputColor, color, 0.5); // <-- Causes error
}
}
The error that I get is from creating the MTLRenderPipelineState with this error message:
Inlining all functions due to use of indirect argument bufferbuffer(5): Argument buffer accessed with non-zero array index.
Short answer: you can't.
The reason you can't is because tier 1 hardware can only emulate argument buffers using regular bind points. With tier2 you can bind any number of textures there, so the driver can't know at bind time how many slots it will need to use, and the hardware itself can't do a dependent read for other GPU objects, like textures and samplers.
A workaround for Tier 1 would be to pass a pointer to the instance inside the Argument Buffer, rather than the entire buffer.
Example, look at the use of Material.
// Argument-buffered resource
struct Material {
metal::sampler sampler [[id(AB_MaterialSampler)]];
metal::texture2d<float> base_color_texture [[id(AB_MaterialBaseColorTexture)]];
metal::texture2d<float> normal_map [[id(AB_MaterialNormalMap)]];
metal::texture2d<float> ao_metallic_roughness_map [[id(AB_MaterialAoMetallicRoughnessMap)]];
float3 base_color_factor [[id(AB_MaterialBaseColorFactor)]];
float metallic_factor [[id(AB_MaterialMetallicFactor)]];
float roughness_factor [[id(AB_MaterialRoughnessFactor)]];
};
// GPU-driven rendering kernel
kernel void icb_frame_kernel(device IcbContainer& icb_container [[buffer(KB_IcbContainer)]],
constant VertexUniforms* vertex_uniforms [[buffer(KB_VertexUniforms)]],
constant FragmentUniforms* fragment_uniforms [[buffer(KB_FragmentUniforms)]],
device Mesh* meshes [[buffer(KB_Meshes)]],
constant Transform* transforms [[buffer(KB_Transforms)]],
device Material* materials [[buffer(KB_Materials)]],
constant ShadowMap* shadow_map [[buffer(KB_ShadowMap)]],
constant Ibl* ibl [[buffer(KB_Ibl)]],
constant Cubemap* cubemap [[buffer(KB_Cubemap)]],
device MTLIndirectCommandBufferExecutionRange& range [[buffer(KB_ExecutionRange)]],
const uint instance_id [[thread_position_in_grid]]) {
device auto& mesh = meshes[instance_id];
device auto* range_length = reinterpret_cast<device atomic_uint*>(&range.length);
const auto index = atomic_fetch_add_explicit(range_length, 1, memory_order_relaxed);
render_command cmd(icb_container.icb, index);
cmd.set_render_pipeline_state(mesh.pipeline_state);
cmd.set_vertex_buffer(mesh.vertex_buffer, VB_Vertices);
cmd.set_vertex_buffer(vertex_uniforms, VB_VertexUniforms);
cmd.set_vertex_buffer(transforms, VB_Transforms);
cmd.set_fragment_buffer(fragment_uniforms, FB_FragmentUniforms);
cmd.set_fragment_buffer(transforms, FB_Transforms);
// Tier 1: use indexed access and pass pointer to instance
cmd.set_fragment_buffer(&materials[instance_id], FB_Material);
// Tier 2: pass entire buffer and use indexed access in fragment shader
cmd.set_fragment_buffer(materials, FB_Material);
cmd.set_fragment_buffer(shadow_map, FB_ShadowMap);
cmd.set_fragment_buffer(ibl, FB_Ibl);
cmd.set_fragment_buffer(cubemap, FB_Cubemap);
if (mesh.is_uint16_index){
constant auto* index_buffer = static_cast<constant ushort*>(mesh.index_buffer);
cmd.draw_indexed_primitives(primitive_type::triangle, mesh.index_count, index_buffer, 1, 0, instance_id);
} else {
constant auto* index_buffer = static_cast<constant uint*>(mesh.index_buffer);
cmd.draw_indexed_primitives(primitive_type::triangle, mesh.index_count, index_buffer, 1, 0, instance_id);
}
}
// Tier 1
fragment half4 pbr_fragment(ProjectedVertex vert [[stage_in]],
constant FragmentUniforms& uniforms [[buffer(FB_FragmentUniforms)]],
constant Material& material [[buffer(FB_Material)]],
constant Ibl& ibl [[buffer(FB_Ibl), function_constant(HAS_IBL)]],
constant ShadowMap& shadow_map [[buffer(FB_ShadowMap), function_constant(HAS_SHADOW_MAP)]]
) {
// Use Material
}
// Tier 2
fragment half4 pbr_fragment(ProjectedVertex vert [[stage_in]],
constant FragmentUniforms& uniforms [[buffer(FB_FragmentUniforms)]],
device Material* materials [[buffer(FB_Material)]],
constant Ibl& ibl [[buffer(FB_Ibl), function_constant(HAS_IBL)]],
constant ShadowMap& shadow_map [[buffer(FB_ShadowMap), function_constant(HAS_SHADOW_MAP)]]
) {
// Use indexed Material
const auto& material = materials[vert.instance_id];
}
I did not have time to edit the example for brevity, but it should be clear enough.
Side note: the Metal spec recommends to use device address space whenever you use pointer arithmetic (indexed access). See page 61 of the spec.
In F# i'm using an external DLL (in this case SDL Graphics library) I'm importing the method I require as follows...
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, int& access, int& w, int& h)
This works fine and I can successfully call the method using the following...
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, &access, &w, &h)
The problem is that the native SDL methods accept null values for many pointer arguments. This is required in some scenarios (which function like overloaded methods). I can't find any way to call these methods from F# passing nulls.
For example, this fails with "does not have null as proper value"
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, null, &w, &h)
I read about the attribute [AllowNullLiteral] but it seems like I can only apply it to types I define, and not pre-defined types which are used in my imported DLL.
Is there any way I can do this?
If you want to specify nulls, you need to use "raw pointers", which are represented by types nativeint and nativeptr<T>.
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, nativeint access, int& w, int& h)
// Call without null
let access = 42
let pAccess = NativePtr.stackalloc<int> 1
NativePtr.write pAccess access
SQL_QueryTexture( textTexture, &format, NativePtr.toNativeInt pAccess, &w, &h )
let returnedAccess = NativePtr.read pAccess
// Call with null
SQL_QueryTexture( textTexture, &format, null, &w, &h )
NOTE: be careful with stackalloc. Allocating memory on the stack is quite handy, because you don't need to explicitly release it, but pointers to it will become invalid once you exit the current function. So you can only pass such pointers to an external function if you're sure that the function won't store the pointer and try to use it later.
If you need to pass a pointer to real heap memory that's not going anywhere, you'll need Marshal.AllocHGlobal. But don't forget to release! (or else :-)
let access = 42
let pAccess = Marshal.AllocHGlobal( sizeof<int> )
NativePtr.write (NativePtr.ofNativeInt pAccess) access
SQL_QueryTexture( textTexture, &format, pAccess, &w, &h )
Marshal.FreeHGlobal( pAccess )
I want to pass a value to a pointer variable, namely from type Mat to CvMat*?
So I have something like the following and want to pass it to the variable Si;;
Mat S=(Mat_<double>(1, 3) << 1,0,1 );
CvMat* Si;
*Si=S;
But this gives a segmentation fault, Am I doing something wrong?
Use
Si = &S if you wanted to change the pointer.
Or initialize Si first to copy S into Si
Si = new Mat_<double>(1, 3);
*Si = S;
Basically before you initialize Si, it is an invalid pointer, and assuming you want to copy a structure to whatever address the pointer refers to, is an invalid operation. You need to "own" a valid memory address (which the new operation creates for you) in order to work on an object.
Don't forget to use delete Si; at some point later on.
You need to allocate storage for the object referenced by the pointer. This can be done by calling cvCreateMat(), as below:
cv::Mat S=(cv::Mat_<double>(1, 3) << 1,0,1 );
CvMat* Si = cvCreateMat(1, 3, CV_64FC1);
*Si=S;
Note that this approach will copy the data from S into Si. If you want to only create a CvMat header without copying the data, do this:
cv::Mat S=(cv::Mat_<double>(1, 3) << 1,0,1 );
CvMat m = S;
CvMat* Si = &m;
I'm trying to read the output from a geometry shader which is using stream-output to output to a buffer.
The output buffer used by the geometry shader is described like this:
D3D10_BUFFER_DESC vbdesc =
{
numPoints * sizeof( MESH_VERTEX ),
D3D10_USAGE_DEFAULT,
D3D10_BIND_VERTEX_BUFFER | D3D10_BIND_STREAM_OUTPUT,
0,
0
};
V_RETURN( pd3dDevice->CreateBuffer( &vbdesc, NULL, &g_pDrawFrom ) );
The geometry shader creates a number of triangles based on a single point (at max 12 triangles per point), and if I understand the SDK correctly I have to create a staging resource in order to read the output from the geometry shader on the CPU.
I have declared another buffer resource (this time setting the STAGING flag) like this:
D3D10_BUFFER_DESC sbdesc =
{
(numPoints * (12*3)) * sizeof( VERTEX_STREAMOUT ),
D3D10_USAGE_STAGING,
NULL,
D3D10_CPU_ACCESS_READ,
0
};
V_RETURN( pd3dDevice->CreateBuffer( &sbdesc, NULL, &g_pStaging ) );
After the first draw call of the application the geometry shader is done creating all triangles and can be drawn. However, after this first draw call I would like to be able to read the vertices output by the geometry shader.
Using the buffer staging resource I'm trying to do it like this (right after the first draw call):
pd3dDevice->CopyResource(g_pStaging, g_pDrawFrom]);
pd3dDevice->Flush();
void *ptr = 0;
HRESULT hr = g_pStaging->Map( D3D10_MAP_READ, NULL, &ptr );
if( FAILED( hr ) )
return hr;
VERTEX_STREAMOUT *mv = (VERTEX_STREAMOUT*)ptr;
g_pStaging->Unmap();
This compiles and doesn't give any errors at runtime. However, I don't seem to be getting any output.
The geometry shader outputs the following:
struct VSSceneStreamOut
{
float4 Pos : POS;
float3 Norm : NORM;
float2 Tex : TEX;
};
and the VERTEX_STREAMOUT is declared like this:
struct VERTEX_STREAMOUT
{
D3DXVECTOR4 Pos;
D3DXVECTOR3 Norm;
D3DXVECTOR2 Tex;
};
Am I missing something here?
Problem solved by creating the staging resource buffer like this:
D3D10_BUFFER_DESC sbdesc;
ZeroMemory( &sbdesc, sizeof(sbdesc) );
g_pDrawFrom->GetDesc( &sbdesc );
sbdesc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
sbdesc.Usage = D3D10_USAGE_STAGING;
sbdesc.BindFlags = 0;
sbdesc.MiscFlags = 0;
V_RETURN( pd3dDevice->CreateBuffer( &sbdesc, NULL, &g_pStaging ) );
Problem was with the ByteWidth.
Given the following Delphil DLL declaration
function csd_HandleData(aBuf: PChar; aLen: integer): integer; stdcall;
what would be the VB6 declaration to use it?
I've tried a variety of declarations, e.g.
Declare Function csd_HandleData Lib "chsdet.dll" (ByVal aBuf As String, ByVal aLen As Integer)
Declare Function csd_HandleData Lib "chsdet.dll" (aBuf As Long, ByVal aLen As Integer)
Declare Function csd_HandleData Lib "chsdet.dll" (aBuf As Byte, ByVal aLen As Integer)
with the relevant code to suit the parameters, but nothing seems to work, i.e. the Delphi debugger says I have a too-largish value in aLen and a null string in aBuf.
I am working toward using a TypeLib to drive the connection, but was prototyping with Declares.
try
Declare Function csd_HandleData Lib "chsdet.dll" (ByVal aBuf As String,
ByVal aLen As Integer) As Integer
Seems you forgot the return value.
VB integer datatype is 16bit, so you should declare it as long which is equivalent to integer in Delphi and other languages.
Declare Function csd_HandleData Lib "chsdet.dll" (ByVal aBuf As String, ByVal aLen As long) as long
For those interested, here's the final IDL for the typelib for CHSDET. What impressed me (after re-discovering Matt Curland's EditTLB tool) was that I can put structures into a typelib, and VB handles them as if I'd declared them in the source code.
I've written to the author of ChsDet and perhaps this will end up as part of the standard distro.
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: chsdet.tlb
[
uuid(316A83D7-8BF4-490E-BDDE-75EBC332C355),
version(1.0),
helpstring("Charset Detector - as the name says - is a stand alone executable module for automatic charset detection of a given text.\r\n\t\r\nIt can be useful for internationalisation support in multilingual applications such as web-script editors or Unicode editors.\r\n\t\r\nGiven input buffer will be analysed to guess used encoding. The result can be used as control parameter for charset conversation procedure.\r\n\t\r\nCharset Detector can be compiled (and hopefully used) for MS Windows (as dll - dynamic link library) or Linux.\r\n\t\r\nBased on Mozilla's i18n component - http://www.mozilla.org/projects/intl/. \r\n\r\nCharset Detector is open source project and distributed under Lesser GPL.\r\nSee the GNU Lesser General Public License for more details - http://www.opensource.org/licenses/lgpl-license.php\r\n\r\nNikolaj Yakowlew \xFFFFFFA9 2006-2008 \r\nTypeLib by Bruce M. Axtens, 2008.")
]
library CHSDET
{
// TLib : // Forward declare all types defined in this typelib
[
dllname("CHSDET.dll"),
version(1.0),
helpstring("Functions in CHSDET.DLL")
]
module CHSDETFunctions {
[entry(0x60000000), helpstring("Returns rAbout record (qv)")]
void _stdcall GetAbout([in, out] rAbout* AboutRec);
[entry(0x60000001), helpstring("Reset detector. Prepares for new analysis.")]
void _stdcall Reset();
[entry(0x60000002), helpstring("Analyse given buffer of specified length.
Return value is of eHandleDataErrors, either
NS_ERROR_OUT_OF_MEMORY (Unable to create internal objects) or NS_OK.
Function can be called more that one time to continue guessing. Charset Detector remembers last state until Reset called.")]
void _stdcall HandleData(
[in] BSTR aBuf,
[in] short aLen,
[out, retval] short* retVal);
[entry(0x60000003), helpstring("Returns either TRUE (Charset Detector is sure about text encoding.) or FALSE.
NB: If input buffer is smaller then 1K, Charset Detector returns FALSE.")]
void _stdcall IsDone([out, retval] short* retVal);
[entry(0x60000004), helpstring("Signal data end. If Charset Detector hasn't sure result (IsDone = FALSE) the best guessed encoding will be set as result.")]
void _stdcall DataEnd();
[entry(0x60000005), helpstring("Returns guessed charset as rCharsetInfo record")]
void _stdcall GetDetectedCharset([out, retval] rCharsetInfo* retVal);
[entry(0x60000006), helpstring("Returns all supported charsets in form "0x0A Name - CodePage"")]
void _stdcall GetKnownCharsets(
[in, out] long* sList,
[out, retval] long* retVal);
[entry(0x60000007), helpstring("Return eBOMKind value matching byte order mark (if any) of input data.")]
void _stdcall GetDetectedBOM([out, retval] eBOMKind* retVal);
[entry(0x60000008), helpstring("Remove CodePage from consideration as a possible match")]
void _stdcall DisableCharsetCP([in] long CodePage);
};
typedef [uuid(91694067-30AB-44A9-A210-F5602935475F)]
struct tagrAbout {
long lMajor;
long lMinor;
long lRelease;
long sAbout;
} rAbout;
typedef [uuid(3C8B7420-D40B-458B-8DE8-9B3D28607396)]
enum {
BOM_Not_Found = 0,
BOM_UCS4_BE = 1,
BOM_UCS4_LE = 2,
BOM_UCS4_2143 = 3,
BOM_UCS4_3412 = 4,
BOM_UTF16_BE = 5,
BOM_UTF16_LE = 6,
BOM_UTF8 = 7
} eBOMKind;
typedef [uuid(9B231DEF-93FB-440D-B06B-D760AECE09D0)]
struct tagrCharsetInfo {
long Name;
short CodePage;
long Language;
} rCharsetInfo;
typedef enum {
NS_OK = 0,
NS_ERROR_OUT_OF_MEMORY = -2147024882
} eHandleDataErrors;
};
I don't know what a PChar is in Delphi, is it just one character? ASCII?? Unicode?
An Integer is 16 bits in VB6, you'll have to declare aLen as Long, which can hold 32 bits.
You also have to declare the return type of the function, in this case you'll want to return a Long value too.
This will probably work:
Declare Function csd_HandleData Lib "chsdet.dll" (aBuf As Byte, ByVal aLen As Long) As Long
I donĀ“t know exactly how Vb works but PChar is a pointer, so try to get the reference instead of the value.
Declare Function csd_HandleData Lib "chsdet.dll" (**ByReference <--guessing here :D** aBuf As String, ByVal aLen As Integer)