Jump to content
  • Advertisement

roastedamoeba

Member
  • Content Count

    39
  • Joined

  • Last visited

Community Reputation

284 Neutral

About roastedamoeba

  • Rank
    Member
  1. It's not for everyone, but I made a unit testing framework for HLSL shaders. It executes your shaders on the CPU, in a little virtual machine, so it can be run on a CI server etc.   http://timjones.tw/blog/archive/2014/01/07/introducing-hlslunit-unit-tests-for-your-hlsl-shader-code
  2. roastedamoeba

    HLSL Tools for Visual Studio

    I'm pleased to announce the release of HLSL Tools for Visual Studio v1.0.   HLSL Tools is a free (and open source) extension for Visual Studio 2013 and 2015 that adds IntelliSense support when editing HLSL shaders.   Since VS2012, Visual Studio has shipped with very basic support for HLSL files - really just syntax highlighting and sometimes-correct automatic formatting. HLSL Tools works with all HLSL versions / shader models, and includes these features: Statement completion Signature help Reference highlighting Navigation bar Navigate to (Ctrl+,) Live syntax and semantic errors Go to definition (F12) Quick info Gray out code excluded by the preprocessor HLSL-specific language options   You can download HLSL Tools: from within Visual Studio, Tools > Extensions and Updates > Search for "HLSL Tools" from the Visual Studio Gallery: https://visualstudiogallery.msdn.microsoft.com/75ddd3be-6eda-4433-a850-458b51186658 as source code from the GitHub repo: https://github.com/tgjones/HlslTools   Perhaps of interest to those writing HLSL-based language tools, HLSL Tools includes a handwritten HLSL parser and semantic analyzer, which underpins all the features mentioned above.   Here are some screen captures showing off some of the IntelliSense features:   Statement completion:       Signature help:       Reference highlighting:       Navigation bar:       Navigate to:       Live errors:       Go to definition:       Quick info:       Preprocessor support:       Options:  
  3. Thank you for the comments!   The most challenging part of implementing an HLSL interpreter was getting dynamic branching working. Because I wanted to emulate GPUs reasonably closely - in particular, running pixel shaders on a 2x2 quad so that partial derivative instructions (ddx, ddy) work correctly - I needed to support running multiple threads at the same time. But that means that when the interpreter executes a conditional branch instruction, some threads might take the branch, and some might not (as it happens, partial derivative instructions don't work inside dynamic branches, but still, I wanted to support multiple threads in general). So just like a real GPU (to the best of my limited knowledge) I execute both branches, and 'mask' the threads that aren't active for the given branch.   In order to make that work, I needed to rewrite the bytecode instructions into the form of basic blocks, where each block has a single entry point and a single exit point. Then, when taking a branch, the interpreter knows the index of the instruction where the branches will come back together. It uses that information to mask / unmask threads. It was quite 'fun' getting it figured out! In case anyone's interested, here are the papers / websites I learnt from: Dynamic Warp Formation: Exploiting Thread Scheduling for Efficient MIMD Control Flow on SIMD Graphics Hardware (yes, it's as fun as it sounds) https://circle.ubc.ca/bitstream/handle/2429/2268/ubc_2008_fall_fung_wilson_wai_lun.pdf?sequence=1 http://s08.idav.ucdavis.edu/fatahalian-gpu-architecture.pdf Analyzing Control Flow in Java Bytecode - http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.35.7827&rep=rep1&type=pdf http://tangentvector.wordpress.com/2013/04/12/a-digression-on-divergence/ http://www.cse.ohio-state.edu/~rountev/788/lectures/ControlFlowAnalysis.pdf (page 19)   SlimShader, the library that actually does most of the heavy lifting for HlslUnit, has a GUI tool that visualises how the instructions are broken down into basic blocks. You can load up the compiled binary for any shader you like (as long as it was compiled for SM4.0 or SM5.0), and see the corresponding control flow - which may (or may not! I only have a vague idea what happens at the hardware level) be similar to the shader code pre-processing that the GPU does.   By the way, the (C#) code for SlimShader, SlimShader.VirtualMachine and HlslUnit is here: https://github.com/tgjones/slimshader   (Incidentally, in addition an HLSL interpreter, I also wrote a JITter, which compiles a new C# class on the fly, for a particular shader. But I don't use the JITter in HlslUnit, because the compilation time is too long when the shader is only getting executed once. I *do* use the JITter in my software rasterizer (https://github.com/tgjones/rasterizr), because it's a lot faster than the interpreter when rendering multiple frames.)    
  4. I'm pleased to announce the release of the first version of HlslUnit, a unit testing library library for HLSL shaders:   http://timjones.tw/blog/archive/2014/01/07/introducing-hlslunit-unit-tests-for-your-hlsl-shader-code   HlslUnit is an open source .NET library that allows you to test your HLSL shaders in isolation, without running your game or application. It executes shaders entirely on the CPU, using an interpreter, and doesn't touch the GPU. Here's an example unit test: [Test] public void CanExecuteVertexShader() { // Arrange. var shader = new Shader(ShaderTestUtility.CompileShader( "Shaders/VS/BasicHLSL.fx", "RenderSceneVS", "vs_4_0")); shader.SetConstantBuffer("$Globals", new VertexConstantBufferGlobals { World = Matrix.Identity, WorldViewProjection = Matrix.LookAtRH(Vector3.UnitZ, Vector3.Zero, Vector3.UnitY) * Matrix.PerspectiveFovRH(MathUtil.PiOverFour, 1, 1, 10) }); var vertexInput = new VertexShaderInput { Position = new Vector4(3, 0, 2, 1), Normal = new Vector3(0, 1, 0), TexCoords = new Vector2(0, 1) }; // Act. var output = shader.Execute<VertexShaderInput, VertexShaderOutput>(vertexInput); // Assert. Assert.That(output, Is.EqualTo(new VertexShaderOutput { Position = new Vector4(7.24264f, 0, -3.222222f, 1), Normal = new Vector3(0, 1, 0), TexCoords = new Vector2(0, 1) })); } I know HLSL and unit tests aren't the most exciting of topics, but I like the idea of bringing automated testing to HLSL code, and hopefully I'm not alone!
  5. I never got round to implementing this properly. My software rasterizer is floating-point, so I can get away with an essentially infinite guard band. If a triangle overlaps the viewport at all, then I rasterize the whole thing. However, because I'm using barycentric rasterization, when I create the screen-space bounds of the triangle, I simply clip the triangle bounds to the viewport bounds. So there's not a lot of extra overhead - obviously, there's a lot of overhead in using floating-point, not fixed-point, in the first place :)   To your question - I think Fabian Giesen meant that it could be done as an internal implementation detail in the rasterizer stage. Maybe when you're going from clip space to screen space - instead of using actual screen space, use the offset screen space that Giesen suggests. Then before generating the fragment / pixel coordinates, map back to real screen space. Possibly :)
  6. Good question! I'm not actually sure what you mean though - Hieroglyph being a Direct3D-based engine, it can presumably take advantage of PIX / VS2012+'s built-in graphics debugging tools?   The details of Rasterizr's pixel history tool are quite specific to it being a software rasteriser, but anyway, here's roughly how it works: Before rendering a frame, you create a TracefileBuilder object, passing it the Device object. The TracefileBuilder listens for every method call which can change device state. It only needs to listen for "public" method calls, such as Draw, because everything else can be derived from that, including the actual rasterized pixels. At this point, no per-pixel data is recorded, so we don't need to store too much in the logfile for each frame. The logfile can be saved to disk or, as in Rasterizr Studio, can be sent directly to the pixel history tool. Here's an example of a serialized logfile: https://github.com/tgjones/rasterizr/blob/master/src/Rasterizr.Studio/Examples/BasicTriangle.rlog The pixel history tool creates a Replayer object, passing it the coordinates of the pixel we're interested in. The Replayer registers itself as interested in any rasterized output which affects that pixel. Then it steps through each logged method call and calls the appropriate methods on the device, to "replay" the frame. The output of all that is a list of PixelHistoryEvent objects, which are shown in the pixel history UI. Each time you click a different pixel, it will actually re-render the frame, recording only per-pixel events that affect the selected pixel. (The full history of every pixel takes up quite a bit of space - my initial implementation did exactly that, but it was far too inefficient, and it's quick enough to simply re-render the frame.) The two files that do most of this work in Rasterizr are these: https://github.com/tgjones/rasterizr/blob/master/src/Rasterizr.Diagnostics.Logging/TracefileBuilder.cs https://github.com/tgjones/rasterizr/blob/master/src/Rasterizr.Diagnostics.Logging/Replayer.cs   The key idea is to record only the public method calls - including the method name and arguments. During replay, everything else can be derived from that. It's important for IDs of resources created during replay to match what was used during recording - but that will happen simply by executing the method calls that create resources in the right order.   There's actually a big limitation in my current implementation - it only works if everything, including creating the device and setting all device state, happens in one frame. MS's Direct3D debugging tools can obviously handle those things happening in a previous frame - I assume they take a snapshot of the initial device state at the beginning of the frame being recorded.   Anyway, sorry, I don't think I answered your question at all! Maybe you could elaborate a bit on what sort of debugging tool you're thinking about?
  7. Firstly, let me say "good luck" - I can say from experience that writing your own software rasterizer is both fun and rewarding.   My own software rasterizer, Rasterizr, is written in C#. The performance is nothing to write home about, but that's partly because I prioritised understandable code over speed. I chose to follow the Direct3D API quite closely, so I have the following stages: Input assembler Vertex shader Geometry shader Rasterizer Pixel shader Output merger I found that this architecture helped keep the code clean and understandable.   For shaders, I did more or less what cgrant suggested. I implemented an HLSL bytecode parser (called SlimShader: https://github.com/tgjones/slimshader). Then I wrote a virtual machine that can execute HLSL shaders on the CPU. Then I wrote a jitter for the virtual machine, to keep performance within reasonable bounds - it produces C# code for a given shader and compiles it on the fly. The end result is that my software rasterizer uses "real" HLSL shaders. (Originally, I used C# for the shaders, but handling pixel shader gradient calculations, which is required for texture mipmapping, was a problem. Also using HLSL shaders is more fun.)   (It's awesome to see Jason Zink replying in this thread - his book "Practical Rendering and Computation with Direct3D 11" was an invaluable reference for me while I was figuring out how Direct3D 11 works, so that I could borrow some of its API for Rasterizr. Thank you Jason!)   I wrote a visual debugging tool, based on Visual Studio's graphics tools - here's a screenshot. It was helpful just yesterday in tracking down a bug related to geometry shaders and render target arrays!   If you want to look at my code, you'll find it here: https://github.com/tgjones/rasterizr. I'd be happy to help if you have any questions.   Good luck!
  8. roastedamoeba

    Taiwan - Province of China?

    Quote:Original post by Hodgman So... technically, it should either be: Taiwan Province, Republic of China -- For people who recognize the Taiwan's independence. or: Taiwan Province, People's Republic of China -- For people who think the Chinese rule Taiwan. In the first instance, it would be simply "Taiwan" (no "Province"). And yes, I know Google are doing that in order to avoid censorship in China - I linked to the same article in my original post. So I guess you're probably both right that GameDev is using Google's list. I don't want to make this more political than it is already, so I'll leave it there :)
  9. I realise this is a politically-charged topic, but here goes anyway... Who decided that, in the country list used by GameDev, Taiwan would be listed as "Taiwan, Province of China"? Is it just a coincidence, coming from the country list that happened to be used, or was it intentional? Just curious, as some of us who live here in Taiwan don't really feel like it's a province of China. Tim
  10. The Assimp viewer does it with these classes: http://assimp.svn.sourceforge.net/viewvc/assimp/trunk/code/GenVertexNormalsProcess.cpp?revision=661&view=markup http://assimp.svn.sourceforge.net/viewvc/assimp/trunk/tools/assimp_view/Normals.cpp?revision=664&view=markup That might give you a start, but as RDragon1 said, it shouldn't be too difficult to extract the normals and render them yourself. In the model viewer I'm working on, I create a line for each normal. The length of the line is proportional to the size of the model. The colour of the line is set from the normal - colour = (normal + 1) / 2. I then render the lines using the "LineList" primitive type.
  11. If you've ever written a game which uses shaders, you'll know that it can be anywhere from tricky to downright impossible to keep track of the permutations that result from adding lights / different materials / different vertex formats to your game. A few years ago, an article appeared in ShaderX3 titled "Generating Shaders from HLSL Fragments", which is available online. I thought this was a neat way (certainly not the only way!) of solving the shader permutation problem, so I have written an open source implementation, building it as a content pipeline extension for XNA. My implementation is called StitchUp, and it allows you to define fragments (built from parameters, textures, vertex inputs, interpolators, header code, and vertex and pixel shaders), which are then combined at build-time into an effect file. There are ways for fragments to communicate with each other. StitchUp supports multiple techniques, multiple render targets, and quite a bit more - for more details, have a look at the blog posts I have linked to below. Here's an example fragment (more examples available in the blog posts and in the GitHub repo): fragment vertex_transform; [parameters] matrix wvp : WORLDVIEWPROJECTION; [vertexattributes] float3 position : POSITION; float3 normal : NORMAL; [interpolators] float3 normal; [vs 2_0] __hlsl__ void main(INPUT input, inout OUTPUT output) { output.position = mul(float4(input.position, 1), wvp); output(normal, input.normal); } __hlsl__ Shawn Hargreaves, the author of the original article, has blogged about StitchUp. The source and binary downloads are here. I have blogged about it a few times: http://www.roastedamoeba.com/blog/archive/2010/11/13/introducing-stitchup-generating-shaders-from-hlsl-shader-fragments http://www.roastedamoeba.com/blog/archive/2010/11/18/some-stitchup-updates http://www.roastedamoeba.com/blog/archive/2010/11/28/stitchup---support-for-multiple-techniques
  12. Thanks Mike, I've logged it there. (That'll teach me to read the SlimDX website properly - earlier I did skim it but only saw the note about posting messages here on GameDev.)
  13. I've gone ahead and applied my suggested fix to the SlimDX code, and it seems to work. I've included the SVN patch for the change inline below (I couldn't see how to attach a file to my reply). Hope this helps, Tim Index: build/vs2010/SlimDX.vcxproj =================================================================== --- build/vs2010/SlimDX.vcxproj (revision 1632) +++ build/vs2010/SlimDX.vcxproj (working copy) @@ -424,6 +424,7 @@ <ClCompile Include="..\..\source\ComObject.cpp" /> <ClCompile Include="..\..\source\CompilationException.cpp" /> <ClCompile Include="..\..\source\Configuration.cpp" /> + <ClCompile Include="..\..\source\directinput\InputRange.cpp" /> <ClCompile Include="..\..\source\ObjectTable.cpp" /> <ClCompile Include="..\..\source\Result.cpp" /> <ClCompile Include="..\..\source\SlimDXException.cpp" /> @@ -1087,6 +1088,7 @@ <ClInclude Include="..\..\source\ComObjectMacros.h" /> <ClInclude Include="..\..\source\CompilationException.h" /> <ClInclude Include="..\..\source\Configuration.h" /> + <ClInclude Include="..\..\source\directinput\InputRange.h" /> <ClInclude Include="..\..\source\Enums.h" /> <ClInclude Include="..\..\source\InternalHelpers.h" /> <ClInclude Include="..\..\source\ObjectTable.h" /> Index: build/vs2010/SlimDX.vcxproj.filters =================================================================== --- build/vs2010/SlimDX.vcxproj.filters (revision 1632) +++ build/vs2010/SlimDX.vcxproj.filters (working copy) @@ -2755,6 +2755,9 @@ <Filter>D3DCompiler\Reflection\Variable</Filter> </ClCompile> <ClCompile Include="..\..\source\AssemblyInfo.cpp" /> + <ClCompile Include="..\..\source\directinput\InputRange.cpp"> + <Filter>DirectInput\Properties</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\source\direct3d10\Direct3D10Exception.h"> @@ -4879,6 +4882,9 @@ <Filter>D3DCompiler\Reflection\Variable</Filter> </ClInclude> <ClInclude Include="..\..\source\stdafx.h" /> + <ClInclude Include="..\..\source\directinput\InputRange.h"> + <Filter>DirectInput\Properties</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <EmbeddedResource Include="..\.\Resources.resx"> Index: source/directinput/InputRange.cpp =================================================================== --- source/directinput/InputRange.cpp (revision 0) +++ source/directinput/InputRange.cpp (revision 0) @@ -0,0 +1,39 @@ +/* +* Copyright (c) 2007-2010 SlimDX Group +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ +#include "stdafx.h" +#include <dinput.h> + +#include "InputRange.h" + +using namespace System; + +namespace SlimDX +{ +namespace DirectInput +{ + InputRange::InputRange(int min, int max) + { + Min = min; + Max = max; + } +} +} \ No newline at end of file Index: source/directinput/InputRange.h =================================================================== --- source/directinput/InputRange.h (revision 0) +++ source/directinput/InputRange.h (revision 0) @@ -0,0 +1,38 @@ +/* +* Copyright (c) 2007-2010 SlimDX Group +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ +#pragma once + +namespace SlimDX +{ + namespace DirectInput + { + public value class InputRange + { + internal: + InputRange( int min, int max ); + + public: + property int Min; + property int Max; + }; + } +} \ No newline at end of file Index: source/directinput/ObjectProperties.cpp =================================================================== --- source/directinput/ObjectProperties.cpp (revision 1632) +++ source/directinput/ObjectProperties.cpp (working copy) @@ -235,11 +235,11 @@ RECORD_DINPUT( hr ); } - int ObjectProperties::LogicalRange::get() + InputRange ObjectProperties::LogicalRange::get() { - DIPROPDWORD dip; + DIPROPRANGE dip; HRESULT hr; - dip.diph.dwSize = sizeof( DIPROPDWORD ); + dip.diph.dwSize = sizeof( DIPROPRANGE ); dip.diph.dwHeaderSize = sizeof( DIPROPHEADER ); dip.diph.dwObj = obj; dip.diph.dwHow = how; @@ -248,16 +248,16 @@ RECORD_DINPUT( hr ); if( FAILED( hr ) ) - return 0; + return InputRange(); - return dip.dwData; + return InputRange(dip.lMin, dip.lMax); } - int ObjectProperties::PhysicalRange::get() + InputRange ObjectProperties::PhysicalRange::get() { - DIPROPDWORD dip; + DIPROPRANGE dip; HRESULT hr; - dip.diph.dwSize = sizeof( DIPROPDWORD ); + dip.diph.dwSize = sizeof( DIPROPRANGE ); dip.diph.dwHeaderSize = sizeof( DIPROPHEADER ); dip.diph.dwObj = obj; dip.diph.dwHow = how; @@ -266,9 +266,9 @@ RECORD_DINPUT( hr ); if( FAILED( hr ) ) - return 0; + return InputRange(); - return dip.dwData; + return InputRange(dip.lMin, dip.lMax); } } } \ No newline at end of file Index: source/directinput/ObjectProperties.h =================================================================== --- source/directinput/ObjectProperties.h (revision 1632) +++ source/directinput/ObjectProperties.h (working copy) @@ -22,6 +22,7 @@ #pragma once #include "Enums.h" +#include "InputRange.h" namespace SlimDX { @@ -105,17 +106,17 @@ /// <summary> /// Gets the range of raw data returned from the object. /// </summary> - property int LogicalRange + property InputRange LogicalRange { - int get(); + InputRange get(); } /// <summary> /// Gets the range of data returned from axis as suggested by the manufacturer. /// </summary> - property int PhysicalRange + property InputRange PhysicalRange { - int get(); + InputRange get(); } }; }
  14. Hi, I guess not many people use this part of the API, but I think I've found a bug in the SlimDX implementation of DirectInput device object ranges. Specifically, in the SlimDX.DirectInput.ObjectProperties class, there are two "range" properties: LogicalRange and PhysicalRange. The implementations of both of these use DIPROPDWORD. However, I believe both of these properties should use DIPROPRANGE, to return a Min and Max (as SetRange(...) already does). I think this should be a fairly small fix, if it is indeed a bug? Here's what I guess the code should look like (I know the real source is in C++, but you get the idea): SlimDX.DirectInput.ObjectProperties.LogicalRange property getter: public InputRange LogicalRange { get { DIPROPRANGE dip; *((int*) &dip) = 20; *((int*) (&dip + 4)) = 0x10; *((int*) (&dip + 8)) = this.obj; *((int*) (&dip + 12)) = this.how; IDirectInputDevice8W* pointer = this.pointer; int modopt(IsLong) hr = **(((int*) pointer))[20](pointer, 0x13, &dip); Result.Record<DirectInputException>(hr, null, null); return ((hr < 0) ? 0 : new InputRange(*(((int*) (&dip + 0x10))), *(((int*) (&dip + 20)))); } } ... where InputRange is a new structure holding min and max. PhysicalRange would be similar. This is pretty much how Managed DirectX used to implement it. Thanks, Tim
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!