diff --git a/render/naga-agal/src/builder.rs b/render/naga-agal/src/builder.rs index bf99d83d4..3031fb4c7 100644 --- a/render/naga-agal/src/builder.rs +++ b/render/naga-agal/src/builder.rs @@ -15,7 +15,8 @@ use num_traits::FromPrimitive; use crate::varying::VaryingRegisters; use crate::{ - types::*, Error, ShaderType, VertexAttributeFormat, MAX_VERTEX_ATTRIBUTES, SHADER_ENTRY_POINT, + types::*, Error, ShaderType, VertexAttributeFormat, MAX_TEXTURES, MAX_VERTEX_ATTRIBUTES, + SHADER_ENTRY_POINT, }; const VERTEX_PROGRAM_CONTANTS: u64 = 128; @@ -52,6 +53,15 @@ struct TextureSamplers { repeat_u_clamp_v_nearest: Handle, } +#[derive(Copy, Clone)] +struct TextureBindingData { + // The `Expression::GlobalVariable` corresponding to the texture that we loaded. + expr: Handle, + // The sample config values extracted from the opcode + // (which may override or be overridden by values set by Context3D.setSamplerStateAt) + sampler_config: SamplerConfig, +} + pub(crate) struct NagaBuilder<'a> { pub(crate) module: Module, pub(crate) func: Function, @@ -74,9 +84,9 @@ pub(crate) struct NagaBuilder<'a> { pub(crate) varying_registers: VaryingRegisters, // Whenever we encounter a texture load at a particular index - // for the first time, we store an `Expression::GlobalVariable` - // here corresponding to the texture that we loaded. - texture_bindings: [Option>; 8], + // for the first time, we store the expression we generate, + // as well as the sampler parameters used. + texture_bindings: [Option; MAX_TEXTURES], // Whenever we read from a particular temporary register // for the first time, we create a new local variable @@ -263,7 +273,7 @@ impl VertexAttributeFormat { pub struct ShaderConfig<'a> { pub shader_type: ShaderType, pub vertex_attributes: &'a [Option; 8], - pub sampler_overrides: &'a [Option; 8], + pub sampler_configs: &'a [SamplerConfig; 8], pub version: AgalVersion, } @@ -273,12 +283,14 @@ pub enum AgalVersion { Agal2, } +struct ParsedBytecode { + version: AgalVersion, + shader_type: ShaderType, + operations: Vec<(Opcode, DestField, SourceField, Source2)>, +} + impl<'a> NagaBuilder<'a> { - pub fn process_agal( - mut agal: &[u8], - vertex_attributes: &[Option; MAX_VERTEX_ATTRIBUTES], - sampler_overrides: &[Option; 8], - ) -> Result { + fn parse_bytecode(mut agal: &[u8]) -> Result { let data = &mut agal; let mut header = [0; 7]; @@ -305,12 +317,7 @@ impl<'a> NagaBuilder<'a> { _ => return Err(Error::InvalidShaderType(header[6])), }; - let mut builder = NagaBuilder::new(ShaderConfig { - shader_type, - vertex_attributes, - sampler_overrides, - version, - }); + let mut operations = Vec::new(); while !data.is_empty() { let mut token = [0; 24]; @@ -331,7 +338,63 @@ impl<'a> NagaBuilder<'a> { token[16..24].try_into().unwrap(), ))?) }; + operations.push((opcode, dest, source1, source2)) + } + Ok(ParsedBytecode { + version, + shader_type, + operations, + }) + } + pub fn extract_sampler_configs(agal: &[u8]) -> Result<[Option; MAX_TEXTURES]> { + let parsed = Self::parse_bytecode(agal)?; + let mut sampler_configs = [None; MAX_TEXTURES]; + for (_opcode, _dest, _source1, source2) in parsed.operations { + if let Source2::Sampler(sampler_field) = source2 { + // When the 'ignore_sampler' field is set, we do not update the sampler config. + // The existing sampler value will end up getting used + // (which comes from a previous Context3D.setSamplerStateAt + // or Context3D.setProgram call) + if sampler_field.special.ignore_sampler { + continue; + } + + let sampler_config = SamplerConfig { + wrapping: sampler_field.wrapping, + filter: sampler_field.filter, + mipmap: sampler_field.mipmap, + }; + let index = sampler_field.reg_num as usize; + if sampler_configs[index].is_none() { + sampler_configs[index] = Some(sampler_config); + } else if sampler_configs[index] != Some(sampler_config) { + return Err(Error::SamplerConfigMismatch { + texture: index, + old: sampler_configs[index].unwrap(), + new: sampler_config, + }); + } + } + } + + Ok(sampler_configs) + } + + pub fn build_module( + agal: &[u8], + vertex_attributes: &[Option; MAX_VERTEX_ATTRIBUTES], + sampler_configs: &[SamplerConfig; 8], + ) -> Result { + let parsed = Self::parse_bytecode(agal)?; + let mut builder = NagaBuilder::new(ShaderConfig { + shader_type: parsed.shader_type, + vertex_attributes, + sampler_configs, + version: parsed.version, + }); + + for (opcode, dest, source1, source2) in parsed.operations { builder.process_opcode(&opcode, &dest, &source1, &source2)?; } builder.finish() @@ -737,8 +800,13 @@ impl<'a> NagaBuilder<'a> { fn emit_texture_load( &mut self, index: usize, - dimension: Dimension, + sampler_field: &SamplerField, ) -> Result> { + let sampler_config = SamplerConfig { + wrapping: sampler_field.wrapping, + filter: sampler_field.filter, + mipmap: sampler_field.mipmap, + }; if self.texture_bindings[index].is_none() { let global_var = self.module.global_variables.append( GlobalVariable { @@ -750,7 +818,7 @@ impl<'a> NagaBuilder<'a> { }), // Note - we assume that a given texture is always sampled with the same dimension // (2d or cube) - ty: match dimension { + ty: match sampler_field.dimension { Dimension::TwoD => self.image2d, Dimension::Cube => self.imagecube, }, @@ -758,13 +826,25 @@ impl<'a> NagaBuilder<'a> { }, Span::UNDEFINED, ); - self.texture_bindings[index] = Some( - self.func + self.texture_bindings[index] = Some(TextureBindingData { + expr: self + .func .expressions .append(Expression::GlobalVariable(global_var), Span::UNDEFINED), - ); + sampler_config, + }); } - Ok(self.texture_bindings[index].unwrap()) + let data = self.texture_bindings[index].as_ref().unwrap(); + // AGAL requires that a given texture ID always be sampled with the same settings + // within a program + if data.sampler_config != sampler_config { + return Err(Error::SamplerConfigMismatch { + texture: index, + old: data.sampler_config, + new: sampler_config, + }); + } + Ok(self.texture_bindings[index].unwrap().expr) } fn emit_source_field_load( @@ -1145,25 +1225,12 @@ impl<'a> NagaBuilder<'a> { panic!("Invalid sample register type {:?}", sampler_field); } - let mut filter = sampler_field.filter; - let mut wrapping = sampler_field.wrapping; - - // See https://github.com/openfl/openfl/issues/1332 - - // FIXME - Flash Player seems to unconditionally use sampler overrides, - // regardless of whether or not `ignore_sampler` is set. I haven't - // found any real SWFs that use it, so let's panic so that get - // get a bug report if it ever happens. - if sampler_field.special.ignore_sampler { - panic!("Found ignore_sampler in {:?}", sampler_field); - } - - if let Some(sampler_override) = - &self.shader_config.sampler_overrides[texture_id as usize] - { - filter = sampler_override.filter; - wrapping = sampler_override.wrapping; - } + // Always take filter/wrapping from the shader config, which takes into account both the values + // from the opcode and any Context3D.setSamplerStateAt calls. + // FIXME - refactor this to just bind the correct sampler at the proper index from the wgpu side. + let sampler_config = self.shader_config.sampler_configs[texture_id as usize]; + let filter = sampler_config.filter; + let wrapping = sampler_config.wrapping; let sampler_binding = match (filter, wrapping) { (Filter::Linear, Wrapping::Clamp) => texture_samplers.clamp_linear, @@ -1229,7 +1296,7 @@ impl<'a> NagaBuilder<'a> { } }; - let image = self.emit_texture_load(texture_id as usize, sampler_field.dimension)?; + let image = self.emit_texture_load(texture_id as usize, sampler_field)?; let tex = self.evaluate_expr(Expression::ImageSample { image, sampler: sampler_binding, diff --git a/render/naga-agal/src/lib.rs b/render/naga-agal/src/lib.rs index 7090b96ca..b5eb4ada0 100644 --- a/render/naga-agal/src/lib.rs +++ b/render/naga-agal/src/lib.rs @@ -29,6 +29,11 @@ pub enum Error { ReadError(std::io::Error), InvalidOpcode(u32), InvalidVersion(u32), + SamplerConfigMismatch { + texture: usize, + old: SamplerConfig, + new: SamplerConfig, + }, } impl From for Error { @@ -43,7 +48,7 @@ pub enum ShaderType { Fragment, } -pub use types::{Filter, Mipmap, SamplerOverride, Wrapping}; +pub use types::{Filter, Mipmap, SamplerConfig, Wrapping}; /** * Compiles an Adobe AGAL shader to a Naga Module. @@ -93,7 +98,13 @@ pub use types::{Filter, Mipmap, SamplerOverride, Wrapping}; pub fn agal_to_naga( agal: &[u8], vertex_attributes: &[Option; MAX_VERTEX_ATTRIBUTES], - sampler_overrides: &[Option; MAX_TEXTURES], + sampler_configs: &[SamplerConfig; MAX_TEXTURES], ) -> Result { - NagaBuilder::process_agal(agal, vertex_attributes, sampler_overrides) + NagaBuilder::build_module(agal, vertex_attributes, sampler_configs) +} + +pub fn extract_sampler_configs( + agal: &[u8], +) -> Result<[Option; MAX_TEXTURES], Error> { + NagaBuilder::extract_sampler_configs(agal) } diff --git a/render/naga-agal/src/types.rs b/render/naga-agal/src/types.rs index db6eefc68..db933e39a 100644 --- a/render/naga-agal/src/types.rs +++ b/render/naga-agal/src/types.rs @@ -186,12 +186,22 @@ pub struct SamplerField { } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct SamplerOverride { +pub struct SamplerConfig { pub wrapping: Wrapping, pub filter: Filter, pub mipmap: Mipmap, } +impl Default for SamplerConfig { + fn default() -> Self { + SamplerConfig { + wrapping: Wrapping::Clamp, + filter: Filter::Nearest, + mipmap: Mipmap::Disable, + } + } +} + impl SamplerField { pub fn parse(val: u64) -> Result { let reg_num = (val & 0xFFFF) as u16; diff --git a/render/naga-agal/tests/snapshots/wgsl__complex_raytrace-2.snap b/render/naga-agal/tests/snapshots/wgsl__complex_raytrace-2.snap index 5cbd9265e..cc6c01006 100644 --- a/render/naga-agal/tests/snapshots/wgsl__complex_raytrace-2.snap +++ b/render/naga-agal/tests/snapshots/wgsl__complex_raytrace-2.snap @@ -1,6 +1,5 @@ --- source: render/naga-agal/tests/wgsl.rs -assertion_line: 105 expression: output --- struct FragmentOutput { @@ -2675,7 +2674,7 @@ fn main(@location(0) param: vec4) -> FragmentOutput { } } let _e5727: vec4 = temporary9_; - let _e5731: vec4 = textureSample(texture0_, sampler2_, _e5727.xyzz.xyz); + let _e5731: vec4 = textureSample(texture0_, sampler3_, _e5727.xyzz.xyz); temporary1_ = _e5731; let _e5732: vec4 = temporary11_; let _e5736: vec4 = constant_registers[16u]; diff --git a/render/naga-agal/tests/wgsl.rs b/render/naga-agal/tests/wgsl.rs index fb8ad99fe..d148cd07c 100644 --- a/render/naga-agal/tests/wgsl.rs +++ b/render/naga-agal/tests/wgsl.rs @@ -22,7 +22,7 @@ pub fn to_wgsl(module: &Module) -> String { // Making this a macro gives us a better span in 'inta' macro_rules! test_shader { ($shader:expr, $attrs:expr, $shader_type:expr $(,)?) => { - let module = agal_to_naga(&$shader, $attrs, &[None; 8]).unwrap(); + let module = agal_to_naga(&$shader, $attrs, &[Default::default(); 8]).unwrap(); let output = to_wgsl(&module); insta::assert_display_snapshot!(output); }; diff --git a/render/wgpu/src/context3d/current_pipeline.rs b/render/wgpu/src/context3d/current_pipeline.rs index bea87fa1d..1b1611a92 100644 --- a/render/wgpu/src/context3d/current_pipeline.rs +++ b/render/wgpu/src/context3d/current_pipeline.rs @@ -1,5 +1,5 @@ use naga::valid::{Capabilities, ValidationFlags, Validator}; -use naga_agal::{Filter, SamplerOverride, Wrapping}; +use naga_agal::{Filter, SamplerConfig, Wrapping}; use ruffle_render::backend::{ Context3DTextureFilter, Context3DTriangleFace, Context3DVertexBufferFormat, Context3DWrapMode, Texture, @@ -84,7 +84,13 @@ pub struct CurrentPipeline { dirty: Cell, - sampler_override: [Option; 8], + // Sampler configuration information for each texture slot. + // This is updated by `Context3D.setSamplerStateAt`, as well + // as in `Context3D.setProgram` (based on the sampling opcodes + // in the program). All texture slots have a sampler set by default + // (which allows rendering with an 'ignoresampler' tex opcode, + // and no calls to Context3D.setSamplerStateAt) + sampler_configs: [SamplerConfig; 8], } #[derive(Clone)] @@ -159,12 +165,20 @@ impl CurrentPipeline { target_format: TextureFormat::Rgba8Unorm, - sampler_override: [None; 8], + sampler_configs: [SamplerConfig::default(); 8], } } pub fn set_shaders(&mut self, shaders: Option>) { self.dirty.set(true); self.shaders = shaders; + if let Some(shaders) = &self.shaders { + for (i, sampler_config) in shaders.fragment_sampler_configs().iter().enumerate() { + // When we call `Context3D.setProgram`, sampler configs from the fragment shader override + // any previously set sampler configs (if 'ignoresampler' was set in the program, then the corresponding + // array entry will be `None`). + self.sampler_configs[i] = sampler_config.unwrap_or(self.sampler_configs[i]); + } + } } pub fn update_texture_at(&mut self, index: usize, texture: Option) { @@ -325,7 +339,7 @@ impl CurrentPipeline { descriptors, ShaderCompileData { vertex_attributes: agal_attributes, - sampler_overrides: self.sampler_override, + sampler_configs: self.sampler_configs, bound_textures: self.bound_textures.clone(), }, ); @@ -538,7 +552,7 @@ impl CurrentPipeline { wrap: ruffle_render::backend::Context3DWrapMode, filter: ruffle_render::backend::Context3DTextureFilter, ) { - let sampler_override = SamplerOverride { + let sampler_config = SamplerConfig { wrapping: match wrap { Context3DWrapMode::Clamp => Wrapping::Clamp, Context3DWrapMode::Repeat => Wrapping::Repeat, @@ -553,10 +567,8 @@ impl CurrentPipeline { // FIXME - implement this mipmap: naga_agal::Mipmap::Disable, }; - if self.sampler_override[sampler] != Some(sampler_override) { - self.dirty.set(true); - self.sampler_override[sampler] = Some(sampler_override); - } + self.dirty.set(true); + self.sampler_configs[sampler] = sampler_config; } } diff --git a/render/wgpu/src/context3d/shader_pair.rs b/render/wgpu/src/context3d/shader_pair.rs index ba684689a..ecafcbbc5 100644 --- a/render/wgpu/src/context3d/shader_pair.rs +++ b/render/wgpu/src/context3d/shader_pair.rs @@ -1,5 +1,5 @@ use lru::LruCache; -use naga_agal::{SamplerOverride, VertexAttributeFormat}; +use naga_agal::{SamplerConfig, VertexAttributeFormat}; use ruffle_render::backend::ShaderModule; use std::{ borrow::Cow, @@ -22,7 +22,9 @@ use crate::descriptors::Descriptors; pub struct ShaderPairAgal { vertex_bytecode: Vec, + fragment_bytecode: Vec, + fragment_sampler_configs: [Option; 8], // Caches compiled wgpu shader modules. The cache key represents all of the data // that we need to pass to `naga_agal::agal_to_naga` to compile a shader. compiled: RefCell>, @@ -38,14 +40,22 @@ pub struct CompiledShaderProgram { impl ShaderPairAgal { pub fn new(vertex_bytecode: Vec, fragment_bytecode: Vec) -> Self { + let fragment_sampler_configs = + naga_agal::extract_sampler_configs(&fragment_bytecode).unwrap(); + Self { vertex_bytecode, fragment_bytecode, + fragment_sampler_configs, // TODO - figure out a good size for this cache. compiled: RefCell::new(LruCache::new(NonZeroUsize::new(2).unwrap())), } } + pub fn fragment_sampler_configs(&self) -> &[Option; 8] { + &self.fragment_sampler_configs + } + pub fn compile( &self, descriptors: &Descriptors, @@ -58,7 +68,7 @@ impl ShaderPairAgal { let vertex_naga_module = naga_agal::agal_to_naga( &self.vertex_bytecode, &data.vertex_attributes, - &data.sampler_overrides, + &data.sampler_configs, ) .unwrap(); let vertex_module = @@ -72,7 +82,7 @@ impl ShaderPairAgal { let fragment_naga_module = naga_agal::agal_to_naga( &self.fragment_bytecode, &data.vertex_attributes, - &data.sampler_overrides, + &data.sampler_configs, ) .unwrap(); let fragment_module = @@ -200,7 +210,7 @@ impl ShaderPairAgal { #[derive(Hash, Eq, PartialEq, Clone)] pub struct ShaderCompileData { - pub sampler_overrides: [Option; 8], + pub sampler_configs: [SamplerConfig; 8], pub vertex_attributes: [Option; MAX_VERTEX_ATTRIBUTES], pub bound_textures: [Option; 8], } diff --git a/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/Test.as b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/Test.as new file mode 100644 index 000000000..4f19533c5 --- /dev/null +++ b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/Test.as @@ -0,0 +1,169 @@ +package { + import com.adobe.utils.AGALMiniAssembler; + + import flash.display.Sprite; + import flash.display.Stage3D; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.display3D.Context3D; + import flash.display3D.Context3DBlendFactor; + import flash.display3D.Context3DCompareMode; + import flash.display3D.Context3DProgramType; + import flash.display3D.Context3DRenderMode; + import flash.display3D.Context3DStencilAction; + import flash.display3D.Context3DTriangleFace; + import flash.display3D.Context3DVertexBufferFormat; + import flash.display3D.Context3DTextureFilter; + import flash.display3D.Context3DWrapMode; + import flash.display3D.IndexBuffer3D; + import flash.display3D.Program3D; + import flash.display3D.VertexBuffer3D; + import flash.events.Event; + import flash.events.KeyboardEvent; + import flash.events.MouseEvent; + import flash.events.TimerEvent; + import flash.geom.Rectangle; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.ui.Keyboard; + import flash.utils.Timer; + import flash.display.MovieClip; + import flash.display.Stage; + import flash.display.BitmapData; + import flash.display.Bitmap; + + // Based on example from https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display3D/Context3D.html#setStencilActions + public class Test extends MovieClip { + public const viewWidth:Number = 500; + public const viewHeight:Number = 600; + + private var stage3D:Stage3D; + private var renderContext:Context3D; + private var indexList:IndexBuffer3D; + private var vertexes:VertexBuffer3D; + + private const VERTEX_SHADER:String = + "add op, va0, vc0 \n" + // copy position to output with offset vector + "mov v0, va1"; // copy uv to varying variable v0 + + private const FRAGMENT_SHADER:String = + "tex oc, v0, fs0 <2d,clamp,linear,mipnone,ignoresampler>"; + + + private const FRAGMENT_SHADER_REPEAT:String = + "tex oc, v0, fs0 <2d,repeat,linear,mipnone>"; + + private var vertexAssembly:AGALMiniAssembler = new AGALMiniAssembler(false); + private var fragmentAssembly:AGALMiniAssembler = new AGALMiniAssembler(false); + private var fragmentAssemblyRepeat:AGALMiniAssembler = new AGALMiniAssembler(false); + private var programPair:Program3D; + + public function Test() { + stage3D = this.stage.stage3Ds[0]; + + // Add event listener before requesting the context + stage3D.addEventListener(Event.CONTEXT3D_CREATE, contextCreated); + stage3D.requestContext3D(Context3DRenderMode.AUTO, "standard"); + + // Compile shaders + vertexAssembly.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER, 2); + fragmentAssembly.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER, 2); + fragmentAssemblyRepeat.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER_REPEAT, 2); + } + + // Note, context3DCreate event can happen at any time, such as when the hardware resources are taken by another process + private function contextCreated(event:Event):void { + renderContext = Stage3D(event.target).context3D; + + renderContext.enableErrorChecking = true; // Can slow rendering - only turn on when developing/testing + renderContext.configureBackBuffer(viewWidth, viewHeight, 4, false); + + // Create vertex index list for the triangles + var triangles:Vector. = Vector.([0, 3, 2, + 0, 1, 3, + ]); + indexList = renderContext.createIndexBuffer(triangles.length); + indexList.uploadFromVector(triangles, 0, triangles.length); + + // Create vertexes + const dataPerVertex:int = 5; + var vertexData:Vector. = Vector.( + [ + // x, y, z u, v + -.1, .1, 0, 0, 2, + .1, .1, 0, 2, 2, + -.1, -.1, 0, 0, 0, + .1, -.1, 0, 2, 0 + ]); + vertexes = renderContext.createVertexBuffer(vertexData.length / dataPerVertex, dataPerVertex); + vertexes.uploadFromVector(vertexData, 0, vertexData.length / dataPerVertex); + + // Identify vertex data inputs for vertex program + renderContext.setVertexBufferAt(0, vertexes, 0, Context3DVertexBufferFormat.FLOAT_3); // va0 is position + renderContext.setVertexBufferAt(1, vertexes, 3, Context3DVertexBufferFormat.FLOAT_2); // va1 is texture uv coords + + const size = 4; + var redGreen = new BitmapData(size, size, true, 0x0); + redGreen.fillRect(new Rectangle(0, 0, size / 2, size / 2), 0xFFFF0000); + redGreen.fillRect(new Rectangle(size / 2, 0, size / 2, size / 2), 0xFF00FF00); + redGreen.fillRect(new Rectangle(0, size / 2, size / 2, size / 2), 0xFF0000FF); + redGreen.fillRect(new Rectangle(size / 2, size / 2, size / 2, size / 2), 0xFFFF00FF); + + var redGreenTexture = renderContext.createTexture(size, size, "bgra", false); + redGreenTexture.uploadFromBitmapData(redGreen); + + // This modification is done after 'redGreenTexture.uploadFromBitmapData(redGreen)', + // so it should have no effect. + redGreen.fillRect(new Rectangle(0, 0, size, size), 0); + + // Upload programs to render context + programPair = renderContext.createProgram(); + programPair.upload(vertexAssembly.agalcode, fragmentAssembly.agalcode); + + var repeatProgram = renderContext.createProgram(); + repeatProgram.upload(vertexAssembly.agalcode, fragmentAssemblyRepeat.agalcode); + + // Clear, setting stencil to 0 + renderContext.clear(.3, .3, .3, 1, 1, 0); + + var offsetVec = Vector.([-0.7, 0.7, 0, 0]); + // FIXME - implement and test anisotropic filters + + renderContext.setTextureAt(0, redGreenTexture); + + for each (var mode in [0, 1, 2, 3, 4, 5]) { + for each (var textureFilter in [Context3DTextureFilter.NEAREST, Context3DTextureFilter.LINEAR]) { + for each (var wrapMode in [Context3DWrapMode.CLAMP, Context3DWrapMode.CLAMP_U_REPEAT_V, Context3DWrapMode.REPEAT, Context3DWrapMode.REPEAT_U_CLAMP_V]) { + if (mode == 0) { + renderContext.setProgram(programPair); + } else if (mode == 1) { + renderContext.setSamplerStateAt(0, wrapMode, textureFilter, "mipnone"); + renderContext.setProgram(repeatProgram); + renderContext.setProgram(programPair); + } else if (mode == 2) { + renderContext.setSamplerStateAt(0, wrapMode, textureFilter, "mipnone"); + renderContext.setProgram(programPair); + } else if (mode == 3) { + renderContext.setSamplerStateAt(0, wrapMode, textureFilter, "mipnone"); + renderContext.setProgram(repeatProgram); + } else if (mode == 4) { + renderContext.setSamplerStateAt(0, wrapMode, textureFilter, "mipnone"); + renderContext.setProgram(programPair); + } else if (mode == 5) { + renderContext.setProgram(repeatProgram); + renderContext.setSamplerStateAt(0, wrapMode, textureFilter, "mipnone"); + } + renderContext.setProgramConstantsFromVector("vertex", 0, offsetVec); + renderContext.drawTriangles(indexList, 0, 2); + + offsetVec[0] += 0.3; + } + } + offsetVec[1] += -0.3; + offsetVec[0] = -0.7; + } + + renderContext.present(); + } + } +} diff --git a/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/output.expected.png b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/output.expected.png new file mode 100644 index 000000000..7f43fc40b Binary files /dev/null and b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/output.expected.png differ diff --git a/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/output.txt b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/output.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.fla b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.fla new file mode 100755 index 000000000..f01a7d779 Binary files /dev/null and b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.fla differ diff --git a/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.swf b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.swf new file mode 100644 index 000000000..77a8f9931 Binary files /dev/null and b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.swf differ diff --git a/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.toml b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.toml new file mode 100644 index 000000000..afd6e2b10 --- /dev/null +++ b/tests/tests/swfs/avm2/stage3d_ignore_sampler_override/test.toml @@ -0,0 +1,9 @@ +num_frames = 1 + +[image_comparisons.output] +tolerance = 1 +# FIXME - investigate why this is so high +max_outliers = 1935 + +[player_options] +with_renderer = { optional = true, sample_count = 1 } \ No newline at end of file diff --git a/tests/tests/swfs/stage3d/shared/com/adobe/utils/AGALMiniAssembler.as b/tests/tests/swfs/stage3d/shared/com/adobe/utils/AGALMiniAssembler.as index f9ee69d41..bafe82618 100644 --- a/tests/tests/swfs/stage3d/shared/com/adobe/utils/AGALMiniAssembler.as +++ b/tests/tests/swfs/stage3d/shared/com/adobe/utils/AGALMiniAssembler.as @@ -372,7 +372,7 @@ package com.adobe.utils if ( optfound == null ) { // todo check that it's a number... - //trace( "Warning, unknown sampler option: "+opts[k] ); + trace( "Warning, unknown sampler option: "+opts[k] ); bias = Number(opts[k]); if ( verbose ) trace( " bias: " + bias );