I'm writing a code generator for a compiler that I'm using as an example in the compiler class I'm teaching. We're using ASM 5.0.3 to generate JVM code. I'm able to evaluate most straight forward expressions and statements, some that call runtime methods, just fine. For the example reference language, we have only boolean and ints, and some simple control structures, with type inference. All "programs" get compiled to a static main method in the resulting class. This is the first year I've used ASM. We previously used jasmine.
I'm having problems with stackmap frames. Here's a sample program that I'm compiling that will exhibit the problem:
i <- 5
if
i < 0 :: j <- 0
i = 0 :: j <- 1
i > 0 :: j <- 2
fi
There is equivalent to a Java program:
int i = 5, j;
if (i < 0) j = 0;
else if (i == 0)j = 1;
else if (i > 0) j = 2;
If I just wrote the program with one alternative, it generates fine. But when I have more than one, as in this case, I get a trace like this:
java.lang.VerifyError: Inconsistent stackmap frames at branch target 39
Exception Details:
Location:
djkcode/Test.main([Ljava/lang/String;)V #12: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: #12
flags: { }
locals: { '[Ljava/lang/String;', integer, integer }
stack: { integer }
Stackmap Frame:
bci: #39
flags: { }
locals: { '[Ljava/lang/String;', integer }
stack: { integer, integer, integer }
Bytecode:
0x0000000: 120b 3c1b 120c 9900 0912 0c3d a700 1b1b
0x0000010: 120c 9900 0912 0d3d a700 0f1b 120c 9900
0x0000020: 0912 0e3d a700 03b1
Stackmap Table:
full_frame(#15,{Object[#16],Integer},{Integer})
full_frame(#27,{Object[#16],Integer},{Integer,Integer})
full_frame(#39,{Object[#16],Integer},{Integer,Integer,Integer})
The ASM calls I make can be seen in this debug output:
DBG> cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
DBG> cw.visit(V1_8, ACC_PUBLIC+ACC_STATIC, "djkcode/Test", null, "java/lang/Object", null
DBG> Generate the default constructor..this works in all cases
DBG>
Start the main method
DBG> mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null
DBG> mv.visitCode()
DBG> mv.visitLdcInsn(5);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> Enter Alternative
DBG> Label endLabel = new Label(); // value = L692342133
DBG> Enter Guard
DBG> Label failLabel = new Label(); // failLabel = L578866604
DBG> mv.visitVarInsn(ILOAD, 1);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitJumpInsn(IFEQ, failLabel);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> mv.visitJumpInsn(GOTO, guardLabelStack.peek()); // label value = L692342133
DBG> mv.visitLabel(failLabel); // failLabel = L578866604
DBG> L578866604:
DBG> Exit Guard
DBG> Enter Guard
DBG> Label failLabel = new Label(); // failLabel = L1156060786
DBG> mv.visitVarInsn(ILOAD, 1);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitJumpInsn(IFEQ, failLabel);
DBG> mv.visitLdcInsn(1);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> mv.visitJumpInsn(GOTO, guardLabelStack.peek()); // label value = L692342133
DBG> mv.visitLabel(failLabel); // failLabel = L1156060786
DBG> L1156060786:
DBG> Exit Guard
DBG> Enter Guard
DBG> Label failLabel = new Label(); // failLabel = L1612799726
DBG> mv.visitVarInsn(ILOAD, 1);
DBG> mv.visitLdcInsn(0);
DBG> mv.visitJumpInsn(IFEQ, failLabel);
DBG> mv.visitLdcInsn(2);
DBG> mv.visitVarInsn(ISTORE, assign.getId().getAddress());
DBG> mv.visitJumpInsn(GOTO, guardLabelStack.peek()); // label value = L692342133
DBG> mv.visitLabel(failLabel); // failLabel = L1612799726
DBG> L1612799726:
DBG> Exit Guard
DBG> mv.visitLabel(endLabel); // endLabel = L692342133
DBG> L692342133:
DBG> Exit Alternative
DBG> mv.visitInsn(RETURN)
DBG> mv.visitMaxs(0, 0);
DBG> mv.visitEnd();
DBG> cw.visitEnd();
If I view the class in the Eclips ASM bytecode browser, I get this:
package asm.djkcode;
...
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(52, ACC_PUBLIC + ACC_STATIC, "djkcode/Test", null, "java/lang/Object", null);
...
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
...
mv.visitJumpInsn(GOTO, l1);
mv.visitLabel(l0);
mv.visitFrame(Opcodes.F_FULL, 2, new Object[] {"[Ljava/lang/String;", Opcodes.INTEGER}, 1, new Object[] {Opcodes.INTEGER});
...
mv.visitJumpInsn(GOTO, l1);
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_FULL, 2, new Object[] {"[Ljava/lang/String;", Opcodes.INTEGER}, 2, new Object[] {Opcodes.INTEGER, Opcodes.INTEGER});
...
mv.visitJumpInsn(GOTO, l1);
mv.visitLabel(l1);
mv.visitFrame(Opcodes.F_FULL, 2, new Object[] {"[Ljava/lang/String;", Opcodes.INTEGER}, 3, new Object[] {Opcodes.INTEGER, Opcodes.INTEGER, Opcodes.INTEGER});
mv.visitInsn(RETURN);
mv.visitMaxs(4, 3);
mv.visitEnd();
}
cw.visitEnd();
It seems to have some visitFrame statements that are incorrect, but the documentation (I'm using ASM 5.0.3) says that if you use COMPUTE_FRAMES you don't have to call visitFrame. What am I missing? I've spent a lot of time trying to get this correct and can imagine how frustrated my students must be getting. I'm beginning to rue the switch from jasmine.
The problem is that you are using the instruction ifeq incorrectly. ifeq compares one argument with zero and will branch accordingly. You are pushing two values onto the stack as you seem to confuse it with if_icmpeq which will test if two operands are equal. Therefore, after each conditional block a dangling int remains on the stack so the code before the block has a different stack depth than the code following it, which implies that you can’t branch over it as branches are not allowed if the source and target have different stack depths (which is exactly what the VerifierError tells you, you can see how each explicit frame has one more Integer on the stack).
So you could change your ifeq instruction to if_icmpeq to reflect your original intention, but it would be more efficient to keep the ifeq instruction and remove the pushing of the zero constant.
Note that it seems to be another mistake that all three instructions do the same ifeq test. I guess, you want to compile your < 0 and > 0 code to the according iflt and ifgt instructions. Again, mind the difference between iflt/ifgt and ificmplt/ificmpgt instructions…
Related
This vulkan tutorial discusses swapchain recreation:
You could also decide to [recreate the swapchain] that if the swap chain is suboptimal, but I've chosen to proceed anyway in that case because we've already acquired an image.
My question is: how would one recreate the swapchain and not proceed in this case of VK_SUBOPTIMAL_KHR?
To see what I mean, let's look at the tutorial's render function:
void drawFrame() {
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreateSwapChain();
return;
/* else if (result == VK_SUBOPTIMAL_KHR) { createSwapchain(); ??? } */
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
throw std::runtime_error("failed to acquire swap chain image!");
}
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences(device, 1, &inFlightFences[currentFrame]);
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
throw std::runtime_error("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = {swapChain};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
result = vkQueuePresentKHR(presentQueue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
} else if (result != VK_SUCCESS) {
throw std::runtime_error("failed to present swap chain image!");
}
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
The trouble is as follows:
vkAcquireImageKHR succeeds, signaling the semaphore and returning a valid, suboptimal image
Recreate the swapchain
We can't present the image from 1 with the swapchain from 2 due to VUID-VkPresentInfoKHR-pImageIndices-01430. We need to call vkAcquireImageKHR again to get a new image.
When we call vkAcquireImageKHR again, the semaphore is in the signaled state which is not allowed (VUID-vkAcquireNextImageKHR-semaphore-01286), we need to 'unsignal' it.
Is the best solution here to destroy and recreate the semaphore?
Ad 3: you can use the old images (and swapchain) if you properly use the oldSwapchain parameter when creating the new swapchain. Which is what I assume the tutorial suggests.
Anyway. What I do is that I paranoidly sanitize that toxic semaphore like this:
// cleanup dangerous semaphore with signal pending from vkAcquireNextImageKHR (tie it to a specific queue)
// https://github.com/KhronosGroup/Vulkan-Docs/issues/1059
void cleanupUnsafeSemaphore( VkQueue queue, VkSemaphore semaphore ){
const VkPipelineStageFlags psw = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &semaphore;
submit_info.pWaitDstStageMask;
vkQueueSubmit( queue, 1, &submit_info, VK_NULL_HANDLE );
}
After that the semaphore can be properly catched with a fence or vkQueueWaitIdle, and then destroyed or reused.
I just destroy them, because the new semaphore count might differ, and I don't really consider swapchain recreation a hotspot (and also I just use vkDeviceWaitIdle in such case).
After people told me to shorten the program I did it and here is the shortened version of the program with the same error as stated above.It only appears after a few moments into the program.If i hit continue the program works fine.However see the movement function?It does't work.The sprite refuses to move in any direction.However if i give a very large floating value in the move,then the sprite is displaced from it's position when i start the program and it stays there in that position with no further movement.For example if i write sprite.move(400.f,400.f) the sprite moves from (0,0) to (400,400) and stays there.It doesn't move any more.
Here's the shortened version of the code:
#include"SFML\Graphics.hpp"
#include<iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(640, 480), "CHECK",sf::Style::Default);
std::cout << "WORKS";
sf::Texture text;
text.loadFromFile("bahamut.png");
sf::Sprite sprite;
sf::Clock frap;
sprite.setTexture(text);
while (window.isOpen())
{
float fps = frap.restart().asSeconds();
sf::Vector2f movements;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A))
{
movements.y = 0;
movements.x = -1 * fps;
}
else
{if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D))
{
movements.y = 0;
movements.x = 1 * fps;
}
else
{ if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S))
{
movements.y = 1 * fps;
movements.x = 0;
}
else
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W))
{
movements.y = -1 * fps;
movements.x = 0;
}
else
{
movements.x = 0;
movements.y = 0;
}
}
}
}
sprite.move(movements);
window.clear();
window.draw(sprite);
window.display();
}
return 0;
}
I improved upon the code and it still produces the same results and error.
On using the dissassembler i saw the crash occurs at
00B37AEE cmp esi,esp
in window.display().
when i create a function and use it to display the sprite,the movement occurs but witthout the unction nada
Your logic says your movement is 0/0 if W is not pressed. The else of the W pressed block overrides all prior settings. And moving the sprite should happen before you display.
I cannot see a reason for the null pointer exception, but that is what the debugger is for. Next time this happens, debug.
Oh and it's int main(), not void. I know the compiler tolerates this error, but it's still an error and undefined behavior.
I'm trying to figure out how to call this AVFoundation function in Swift. I've spent a ton of time fiddling with declarations and syntax, and got this far. The compiler is mostly happy, but I'm left with one last quandary.
public func captureOutput(
captureOutput: AVCaptureOutput!,
didOutputSampleBuffer sampleBuffer: CMSampleBuffer!,
fromConnection connection: AVCaptureConnection!
) {
let samplesInBuffer = CMSampleBufferGetNumSamples(sampleBuffer)
var audioBufferList: AudioBufferList
var buffer: Unmanaged<CMBlockBuffer>? = nil
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
sampleBuffer,
nil,
&audioBufferList,
UInt(sizeof(audioBufferList.dynamicType)),
nil,
nil,
UInt32(kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment),
&buffer
)
// do stuff
}
The compiler complains for the 3rd and 4th arguments:
Address of variable 'audioBufferList' taken before it is initialized
and
Variable 'audioBufferList' used before being initialized
So what am I supposed to do here?
I'm working off of this StackOverflow answer but it's Objective-C. I'm trying to translate it into Swift, but run into this problem.
Or is there possibly a better approach? I need to read the data from the buffer, one sample at a time, so I'm basically trying to get an array of the samples that I can iterate over.
Disclaimer: I have just tried to translate the code from Reading audio samples via AVAssetReader to Swift, and verified that it compiles. I have not
tested if it really works.
// Needs to be initialized somehow, even if we take only the address
var audioBufferList = AudioBufferList(mNumberBuffers: 1,
mBuffers: AudioBuffer(mNumberChannels: 0, mDataByteSize: 0, mData: nil))
var buffer: Unmanaged<CMBlockBuffer>? = nil
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
sampleBuffer,
nil,
&audioBufferList,
UInt(sizeof(audioBufferList.dynamicType)),
nil,
nil,
UInt32(kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment),
&buffer
)
// Ensure that the buffer is released automatically.
let buf = buffer!.takeRetainedValue()
// Create UnsafeBufferPointer from the variable length array starting at audioBufferList.mBuffers
let audioBuffers = UnsafeBufferPointer<AudioBuffer>(start: &audioBufferList.mBuffers,
count: Int(audioBufferList.mNumberBuffers))
for audioBuffer in audioBuffers {
// Create UnsafeBufferPointer<Int16> from the buffer data pointer
var samples = UnsafeMutableBufferPointer<Int16>(start: UnsafeMutablePointer(audioBuffer.mData),
count: Int(audioBuffer.mDataByteSize)/sizeof(Int16))
for sample in samples {
// ....
}
}
Swift3 solution:
func loopAmplitudes(audioFileUrl: URL) {
let asset = AVAsset(url: audioFileUrl)
let reader = try! AVAssetReader(asset: asset)
let track = asset.tracks(withMediaType: AVMediaTypeAudio)[0]
let settings = [
AVFormatIDKey : kAudioFormatLinearPCM
]
let readerOutput = AVAssetReaderTrackOutput(track: track, outputSettings: settings)
reader.add(readerOutput)
reader.startReading()
while let buffer = readerOutput.copyNextSampleBuffer() {
var audioBufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: AudioBuffer(mNumberChannels: 0, mDataByteSize: 0, mData: nil))
var blockBuffer: CMBlockBuffer?
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
buffer,
nil,
&audioBufferList,
MemoryLayout<AudioBufferList>.size,
nil,
nil,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
&blockBuffer
);
let buffers = UnsafeBufferPointer<AudioBuffer>(start: &audioBufferList.mBuffers, count: Int(audioBufferList.mNumberBuffers))
for buffer in buffers {
let samplesCount = Int(buffer.mDataByteSize) / MemoryLayout<Int16>.size
let samplesPointer = audioBufferList.mBuffers.mData!.bindMemory(to: Int16.self, capacity: samplesCount)
let samples = UnsafeMutableBufferPointer<Int16>(start: samplesPointer, count: samplesCount)
for sample in samples {
//do something with you sample (which is Int16 amplitude value)
}
}
}
}
The answers posted here make assumptions about the size of the necessary AudioBufferList -- which may have allowed them to have work in their particular circumstance, but didn't work for me when receiving audio from a AVCaptureSession. (Apple's own sample code didn't work either.)
The documentation on CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer is not obvious, but it turns out you can ask the function it how big AudioListBuffer item should be first, and then call it a second time with an AudioBufferList allocated to the size it wants.
Below is a C++ example (sorry, don't know Swift) that shows a more general solution that worked for me.
// ask the function how big the audio buffer list should be for this
// sample buffer ref
size_t requiredABLSize = 0;
err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer,
&requiredABLSize,
NULL,
NULL,
kCFAllocatorSystemDefault,
kCFAllocatorSystemDefault,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
NULL);
// allocate an audio buffer list of the required size
AudioBufferList* audioBufferList = (AudioBufferList*) malloc(requiredABLSize);
// ensure that blockBuffer is NULL in case the function fails
CMBlockBufferRef blockBuffer = NULL;
// now let the function allocate fill in the ABL for you
err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer,
NULL,
audioBufferList,
requiredABLSize,
kCFAllocatorSystemDefault,
kCFAllocatorSystemDefault,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
&blockBuffer);
// if we succeeded...
if (err == noErr) {
// la la la... read your samples...
}
// release the allocated block buffer
if (blockBuffer != NULL) {
CFRelease(blockBuffer);
blockBuffer = NULL;
}
// release the allocated ABL
if (audioBufferList != NULL) {
free(audioBufferList);
audioBufferList = NULL;
}
I'll leave it up to the Swift experts to offer an implementation in that language.
Martin's answer works and does exactly what I asked in the question, however, after posting the question and spending more time with the problem (and before seeing Martin's answer), I came up with this:
public func captureOutput(
captureOutput: AVCaptureOutput!,
didOutputSampleBuffer sampleBuffer: CMSampleBuffer!,
fromConnection connection: AVCaptureConnection!
) {
let samplesInBuffer = CMSampleBufferGetNumSamples(sampleBuffer)
self.currentZ = Double(samplesInBuffer)
let buffer: CMBlockBufferRef = CMSampleBufferGetDataBuffer(sampleBuffer)
var lengthAtOffset: size_t = 0
var totalLength: size_t = 0
var data: UnsafeMutablePointer<Int8> = nil
if( CMBlockBufferGetDataPointer( buffer, 0, &lengthAtOffset, &totalLength, &data ) != noErr ) {
println("some sort of error happened")
} else {
for i in stride(from: 0, to: totalLength, by: 2) {
// do stuff
}
}
}
This is a slightly different approach, and probably still has room for improvement, but the main point here is that at least on an iPad Mini (and probably other devices), each time this method is called, we get 1,024 samples. But those samples come in an array of 2,048 Int8 values. Every other one is the left/right byte that needs to be combined into to make an Int16 to turn the 2,048 half-samples into 1,024 whole samples.
it works for me. try it:
let musicUrl: NSURL = mediaItemCollection.items[0].valueForProperty(MPMediaItemPropertyAssetURL) as! NSURL
let asset: AVURLAsset = AVURLAsset(URL: musicUrl, options: nil)
let assetOutput = AVAssetReaderTrackOutput(track: asset.tracks[0] as! AVAssetTrack, outputSettings: nil)
var error : NSError?
let assetReader: AVAssetReader = AVAssetReader(asset: asset, error: &error)
if error != nil {
print("Error asset Reader: \(error?.localizedDescription)")
}
assetReader.addOutput(assetOutput)
assetReader.startReading()
let sampleBuffer: CMSampleBufferRef = assetOutput.copyNextSampleBuffer()
var audioBufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: AudioBuffer(mNumberChannels: 0, mDataByteSize: 0, mData: nil))
var blockBuffer: Unmanaged<CMBlockBuffer>? = nil
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
sampleBuffer,
nil,
&audioBufferList,
sizeof(audioBufferList.dynamicType), // instead of UInt(sizeof(audioBufferList.dynamicType))
nil,
nil,
UInt32(kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment),
&blockBuffer
)
I do this (swift 4.2):
let n = CMSampleBufferGetNumSamples(audioBuffer)
let format = CMSampleBufferGetFormatDescription(audioBuffer)!
let asbd = CMAudioFormatDescriptionGetStreamBasicDescription(format)!.pointee
let nChannels = Int(asbd.mChannelsPerFrame) // probably 2
let bufferlistSize = AudioBufferList.sizeInBytes(maximumBuffers: nChannels)
let abl = AudioBufferList.allocate(maximumBuffers: nChannels)
for i in 0..<nChannels {
abl[i] = AudioBuffer(mNumberChannels: 0, mDataByteSize: 0, mData: nil)
}
var block: CMBlockBuffer?
var status = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(audioBuffer, bufferListSizeNeededOut: nil, bufferListOut: abl.unsafeMutablePointer, bufferListSize: bufferlistSize, blockBufferAllocator: nil, blockBufferMemoryAllocator: nil, flags: 0, blockBufferOut: &block)
assert(noErr == status)
// use AudioBufferList here (abl.unsafePointer), e.g. with ExtAudioFileWrite or what have you
I'm programming a two-pass effect in DirectX 11 (SharpDX). It's supposed to write the depth to a texture in the first pass and then use that texture to extract data on the second one in the pixel shader.
What I get is a white screen, with nothing but the interface and I don't know why nothing is being printed. What could be the problem? I would say I should get at least something from the Depth Texture. Is there an easier way to obtain what I'm aiming for?
For information about what I'm doing:
This is how I'm setting the depth texture values:
this.depthBuffer = new Texture2D(device, new Texture2DDescription()
{
Format = Format.R32_Typeless,
ArraySize = 1,
MipLevels = 1,
Width = (int)host.ActualWidth,
Height = (int)host.ActualHeight,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.DepthStencil | BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
});
this.depthBufferShaderResourceView = new ShaderResourceView(this.device, this.depthBuffer, new ShaderResourceViewDescription()
{
Format = Format.R32_Float,
Dimension = ShaderResourceViewDimension.Texture2D,
Texture2D = new ShaderResourceViewDescription.Texture2DResource()
{
MipLevels = 1,
MostDetailedMip = 0,
}
});
var depthStencilDesc = new DepthStencilStateDescription()
{
DepthComparison = Comparison.LessEqual,
DepthWriteMask = global::SharpDX.Direct3D11.DepthWriteMask.All,
IsDepthEnabled = true,
};
And here is how I sample the depth in the .fx file:
int3 posTex = int3(input.p.xy, 0);
float depthPixel = DepthTexture.Load(posTex);
float4 color = float4(depthPixel, depthPixel , depthPixel, 1.0f );
return color;
And here the way I'm now setting the Depth Buffer stencil view as a Render Target in 2 passes. In the first I try to set the depthstencilview as a target. In the second pass I'm trying to set teh depth texture as a shader resource to read from it.
this.device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(this.vertexBuffer, LinesVertex.SizeInBytes, 0));
// PASS 0
this.device.ImmediateContext.OutputMerger.SetTargets(depthBufferStencilView);
this.device.ImmediateContext.ClearDepthStencilView(this.depthBufferStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
this.technique.GetPassByIndex(0).Apply(this.device.ImmediateContext);
this.device.ImmediateContext.DrawIndexed(this.geometry.Indices.Length, 0, 0);
// PASS 1
this.device.ImmediateContext.OutputMerger.ResetTargets(); // unbinding the depthStencilView
this.device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(this.vertexBuffer, LinesVertex.SizeInBytes, 0));
this.depthStencilShaderResourceVariable = effect.GetVariableByName("DepthTexture").AsShaderResource();
this.depthStencilShaderResourceVariable.SetResource(this.depthBufferShaderResourceView);
this.technique.GetPassByIndex(1).Apply(this.device.ImmediateContext);
this.device.ImmediateContext.DrawIndexed(this.geometry.Indices.Length, 0, 0);
Finally, this is how I set the two passes in the .fx file:
technique11 RenderMyTechnique
{
pass P0
{
SetDepthStencilState( DSSDepthLessEqual, 0 );
SetVertexShader ( CompileShader( vs_4_0, VShader() ) );
SetHullShader ( NULL );
SetDomainShader ( NULL );
SetGeometryShader ( NULL );
SetPixelShader ( NULL );
}
pass P1
{
SetDepthStencilState( DSSDepthLessEqual, 0 );
SetVertexShader ( CompileShader( vs_4_0, VShader() ) );
SetHullShader ( NULL );
SetDomainShader ( NULL );
SetGeometryShader ( CompileShader( gs_4_0, GShader() ) );
SetPixelShader ( CompileShader( ps_4_0, PShader() ) );
}
}
When you call:
this.device.ImmediateContext.OutputMerger.ResetTargets();
After that your perform your draw directly, you don't bind any render target to it, so you need to call:
this.device.ImmediateContext.OutputMerger.SetTargets(renderView);
renderview being any rendertarget view (can be your swapchain). For the moment you do a draw but to "nothing".
Second potential issue, you redraw the same model, but with no depth stencil, so results might be different (specially since in one version you use a Geometry Shader and not in the other one). So your depth data from previous pass might not be valid at all.
I downloaded a small sample program in C++ that opens an ODBC connection to SQL Server, issues a query, and prints the result.
When the query string includes a parameter and I call SQLBindParameter, the query execution returns SQL_SUCCESS but subsequentally fails to get any records.
If I do the equivalent query directly in SQL Management studio it works.
Here's the code. Note that to keep things short I have removed error checking in places where I've not been having problems. The real connection string is obfuscated.
SQLHANDLE sqlenvhandle = 0;
SQLHANDLE sqlconnectionhandle = 0;
SQLHANDLE sqlstatementhandle = 0;
SQLHANDLE sqlstatementhandle2 = 0;
SQLRETURN retcode = 0;
SQLWCHAR retconstring[1024];
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &sqlenvhandle);
retcode = SQLSetEnvAttr(sqlenvhandle,SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, sqlenvhandle, &sqlconnectionhandle);
retcode = SQLDriverConnect(sqlconnectionhandle, NULL, (SQLWCHAR*)_T("--connectionstring--"),
SQL_NTS, retconstring, 1024, NULL, SQL_DRIVER_NOPROMPT);
retcode = SQLAllocHandle(SQL_HANDLE_STMT, sqlconnectionhandle, &sqlstatementhandle2);
SQLWCHAR *query = _T("SELECT * FROM gbm_models WHERE stagename like ?");
SQLWCHAR *searchname = _T("Yuk%\0");
retcode = SQLPrepare (sqlstatementhandle2, query, SQL_NTS);
SQLINTEGER xxx = SQL_NTS;
retcode = SQLBindParameter( sqlstatementhandle2, 1, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_VARCHAR, 5, 0, searchname, 0, &xxx );
SQLWCHAR sqlbuffer[400];
SQLINTEGER buflen = 0;
retcode = SQLExecute (sqlstatementhandle2);
char name[512];
int id;
while(SQLFetch(sqlstatementhandle2) == SQL_SUCCESS)
{
SQLGetData(sqlstatementhandle2, 1, SQL_C_ULONG, &id, 0, NULL);
SQLGetData(sqlstatementhandle2, 4, SQL_C_CHAR, name, 500, NULL);
cout << id << " " << name << endl;
}
SQLFreeHandle(SQL_HANDLE_STMT, sqlstatementhandle2 );
SQLFreeHandle(SQL_HANDLE_STMT, sqlstatementhandle );
SQLDisconnect(sqlconnectionhandle);
SQLFreeHandle(SQL_HANDLE_DBC, sqlconnectionhandle);
SQLFreeHandle(SQL_HANDLE_ENV, sqlenvhandle);
None of these calls return an error. What happens is that the call to SQLFetch returns SQL_NO_DATA. If I replace the "?" in the initial query string with the actual name string, and comment out the SQLBindParameter call, it works fine and retrieves the expected data. So obviously, the search string is not making its way into the query correctly.
Any ideas what's wrong? Does anybody know a way to get the processed query string with the parameter substitution done? I thought the SQLNativeSql function did that, but when I call it, I just get back the original query, so I'm not sure if it's working right or what. Could it be a Unicode thing?
See the datatypes at http://msdn.microsoft.com/en-us/library/windows/desktop/ms714556(v=vs.85).aspx
SQL_C_CHAR is not compatible with the SQLWCHAR so your binding fails to do a proper job - it is using a pointer to double-byte wide characters and a resulting "C-string" is not what you'd expect it to be.
Use compatible datatypes, in your case just go with:
SQLCHAR *searchname = "Yuk%";