internal/graphicsdriver/opengl/program.go
func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM, filter graphics.Filter, address graphics.Address) error {
	destination := d.state.destination
	if destination == nil {
		panic("destination image is not set")
	}
	source := d.state.source
	if source == nil {
		panic("source image is not set")
	}

	if err := destination.setViewport(); err != nil {
		return err
	}
	dstW := destination.width
	srcW, srcH := source.width, source.height

	d.context.blendFunc(mode)

	program := d.state.programs[programKey{
		useColorM: colorM != nil,
		filter:    filter,
		address:   address,
	}]
	if d.state.lastProgram != program {
		d.context.useProgram(program)
		if d.state.lastProgram == zeroProgram {
			theArrayBufferLayout.enable(&d.context, program)
			d.context.bindBuffer(arrayBuffer, d.state.arrayBuffer)
			d.context.bindBuffer(elementArrayBuffer, d.state.elementArrayBuffer)
			d.context.uniformInt(program, "texture", 0)
		}

		d.state.lastProgram = program
		d.state.lastViewportWidth = 0
		d.state.lastViewportHeight = 0
		d.state.lastColorMatrix = nil
		d.state.lastColorMatrixTranslation = nil
		d.state.lastSourceWidth = 0
		d.state.lastSourceHeight = 0
	}

	vw := destination.framebuffer.width
	vh := destination.framebuffer.height
	if d.state.lastViewportWidth != vw || d.state.lastViewportHeight != vh {
		d.context.uniformFloats(program, "viewport_size", []float32{float32(vw), float32(vh)})
		d.state.lastViewportWidth = vw
		d.state.lastViewportHeight = vh
	}

	if colorM != nil {
		esBody, esTranslate := colorM.UnsafeElements()
		if !areSameFloat32Array(d.state.lastColorMatrix, esBody) {
			d.context.uniformFloats(program, "color_matrix_body", esBody)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrix = esBody
		}
		if !areSameFloat32Array(d.state.lastColorMatrixTranslation, esTranslate) {
			d.context.uniformFloats(program, "color_matrix_translation", esTranslate)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrixTranslation = esTranslate
		}
	}

	if filter != graphics.FilterNearest {
		sw := graphics.InternalImageSize(srcW)
		sh := graphics.InternalImageSize(srcH)
		if d.state.lastSourceWidth != sw || d.state.lastSourceHeight != sh {
			d.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})
			d.state.lastSourceWidth = sw
			d.state.lastSourceHeight = sh
		}
	}

	if filter == graphics.FilterScreen {
		scale := float32(dstW) / float32(srcW)
		d.context.uniformFloat(program, "scale", scale)
	}

	// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
	// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
	d.context.bindTexture(source.textureNative)

	d.state.source = nil
	d.state.destination = nil
	return nil
}
func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM, filter graphics.Filter, address graphics.Address) error {
	destination := d.state.destination
	if destination == nil {
		panic("destination image is not set")
	}
	source := d.state.source
	if source == nil {
		panic("source image is not set")
	}

	if err := destination.setViewport(); err != nil {
		return err
	}
	dstW := destination.width
	srcW, srcH := source.width, source.height

	d.context.blendFunc(mode)

	program := d.state.programs[programKey{
		useColorM: colorM != nil,
		filter:    filter,
		address:   address,
	}]
	if d.state.lastProgram != program {
		d.context.useProgram(program)
		if d.state.lastProgram == zeroProgram {
			theArrayBufferLayout.enable(&d.context, program)
			d.context.bindBuffer(arrayBuffer, d.state.arrayBuffer)
			d.context.bindBuffer(elementArrayBuffer, d.state.elementArrayBuffer)
			d.context.uniformInt(program, "texture", 0)
		}

		d.state.lastProgram = program
		d.state.lastViewportWidth = 0
		d.state.lastViewportHeight = 0
		d.state.lastColorMatrix = nil
		d.state.lastColorMatrixTranslation = nil
		d.state.lastSourceWidth = 0
		d.state.lastSourceHeight = 0
	}

	vw := destination.framebuffer.width
	vh := destination.framebuffer.height
	if d.state.lastViewportWidth != vw || d.state.lastViewportHeight != vh {
		d.context.uniformFloats(program, "viewport_size", []float32{float32(vw), float32(vh)})
		d.state.lastViewportWidth = vw
		d.state.lastViewportHeight = vh
	}

	if colorM != nil {
		esBody, esTranslate := colorM.UnsafeElements()
		if !areSameFloat32Array(d.state.lastColorMatrix, esBody) {
			d.context.uniformFloats(program, "color_matrix_body", esBody)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrix = esBody
		}
		if !areSameFloat32Array(d.state.lastColorMatrixTranslation, esTranslate) {
			d.context.uniformFloats(program, "color_matrix_translation", esTranslate)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrixTranslation = esTranslate
		}
	}

	if filter != graphics.FilterNearest {
		sw := graphics.InternalImageSize(srcW)
		sh := graphics.InternalImageSize(srcH)
		if d.state.lastSourceWidth != sw || d.state.lastSourceHeight != sh {
			d.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})
			d.state.lastSourceWidth = sw
			d.state.lastSourceHeight = sh
		}
	}

	if filter == graphics.FilterScreen {
		scale := float32(dstW) / float32(srcW)
		d.context.uniformFloat(program, "scale", scale)
	}

	// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
	// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
	d.context.bindTexture(source.textureNative)

	d.state.source = nil
	d.state.destination = nil
	return nil
}
// useProgram uses the program (programTexture).